diff --git a/browser/base/content/tabbrowser.js b/browser/base/content/tabbrowser.js index 75513e7d2263..96928a9cf879 100644 --- a/browser/base/content/tabbrowser.js +++ b/browser/base/content/tabbrowser.js @@ -63,6 +63,7 @@ XPCOMUtils.defineLazyModuleGetters(this, { E10SUtils: "resource://gre/modules/E10SUtils.jsm", + PictureInPicture: "resource://gre/modules/PictureInPicture.jsm", }); XPCOMUtils.defineLazyServiceGetters(this, { MacSharingService: [ @@ -5290,7 +5291,8 @@ (aBrowser == this.selectedBrowser && window.windowState != window.STATE_MINIMIZED && !window.isFullyOccluded) || - this._printPreviewBrowsers.has(aBrowser) + this._printPreviewBrowsers.has(aBrowser) || + this.PictureInPicture.isOriginatingBrowser(aBrowser) ); }, diff --git a/browser/base/content/upgradeDialog.js b/browser/base/content/upgradeDialog.js index 3184d886531b..60139630d799 100644 --- a/browser/base/content/upgradeDialog.js +++ b/browser/base/content/upgradeDialog.js @@ -212,7 +212,7 @@ function onLoad(ready) { 1; const enableVariant = () => enableTheme( - THEME_IDS[variations.getAttribute("next")][getVariantIndex()] + THEME_IDS[variations.getAttribute("next") ?? 0][getVariantIndex()] ); // Prepare random theme selection that's not (first) default. diff --git a/browser/components/BrowserGlue.jsm b/browser/components/BrowserGlue.jsm index fde57768680e..9fbc5871566c 100644 --- a/browser/components/BrowserGlue.jsm +++ b/browser/components/BrowserGlue.jsm @@ -993,35 +993,6 @@ BrowserGlue.prototype = { Services.prefs.savePrefFile(null); }, - _setSyncAutoconnectDelay: function BG__setSyncAutoconnectDelay() { - // Assume that a non-zero value for services.sync.autoconnectDelay should override - if (Services.prefs.prefHasUserValue("services.sync.autoconnectDelay")) { - let prefDelay = Services.prefs.getIntPref( - "services.sync.autoconnectDelay" - ); - - if (prefDelay > 0) { - return; - } - } - - // delays are in seconds - const MAX_DELAY = 300; - let delay = 3; - for (let win of Services.wm.getEnumerator("navigator:browser")) { - // browser windows without a gBrowser almost certainly means we are - // shutting down, so instead of just ignoring that window we abort. - if (win.closed || !win.gBrowser) { - return; - } - delay += win.gBrowser.tabs.length; - } - delay = delay <= MAX_DELAY ? delay : MAX_DELAY; - - const { Weave } = ChromeUtils.import("resource://services-sync/main.js"); - Weave.Service.scheduler.delayedAutoConnect(delay); - }, - // nsIObserver implementation observe: async function BG_observe(subject, topic, data) { switch (topic) { @@ -1064,9 +1035,6 @@ BrowserGlue.prototype = { this._setPrefToSaveSession(); } break; - case "weave:service:ready": - this._setSyncAutoconnectDelay(); - break; case "fxaccounts:onverified": this._onThisDeviceConnected(); break; @@ -1232,7 +1200,6 @@ BrowserGlue.prototype = { "browser:purge-session-history", "quit-application-requested", "quit-application-granted", - "weave:service:ready", "fxaccounts:onverified", "fxaccounts:device_connected", "fxaccounts:verify_login", @@ -2765,6 +2732,15 @@ BrowserGlue.prototype = { this._collectTelemetryPiPEnabled(); }, }, + // Schedule a sync (if enabled) after we've loaded + { + task: async () => { + if (WeaveService.enabled) { + await WeaveService.whenLoaded(); + WeaveService.Weave.Service.scheduler.autoConnect(); + } + }, + }, { condition: AppConstants.platform == "win", diff --git a/browser/components/pocket/content/panels/css/main.compiled.css b/browser/components/pocket/content/panels/css/main.compiled.css index 97558b9b6ee4..c64732abfa7f 100644 --- a/browser/components/pocket/content/panels/css/main.compiled.css +++ b/browser/components/pocket/content/panels/css/main.compiled.css @@ -1987,6 +1987,45 @@ button { margin: 20px 0; } +.stp_tag_picker .stp_tag_picker_tags { + display: flex; + flex-wrap: wrap; + padding: 8px; + border: 1px solid #8F8F9D; + border-radius: 4px; + font-style: normal; + font-weight: normal; + font-size: 1em; + line-height: 1.2em; + color: #15141A; + margin-bottom: 10px; +} +.stp_tag_picker .stp_tag_picker_tag { + background: #F0F0F4; + border-radius: 4px; + color: #15141A; + display: inline-block; + font-size: 0.7em; + font-style: normal; + line-height: 1em; + font-weight: 600; + margin-bottom: 8px; + margin-inline-end: 8px; + padding: 4px 8px; + transition: background-color 200ms ease-in-out; +} +.stp_tag_picker .stp_tag_picker_tag_remove { + padding: 5px; + color: #5B5B66; + font-weight: 400; +} +.stp_tag_picker .stp_tag_picker_tag_duplicate { + background-color: #bbb; +} +.stp_tag_picker .stp_tag_picker_input { + flex-grow: 1; +} + .stp_popular_topics { padding: 0; } diff --git a/browser/components/pocket/content/panels/css/main.scss b/browser/components/pocket/content/panels/css/main.scss index ebb8425162c0..c4a8896985dd 100644 --- a/browser/components/pocket/content/panels/css/main.scss +++ b/browser/components/pocket/content/panels/css/main.scss @@ -8,6 +8,7 @@ // Components +@import "../js/components/TagPicker/TagPicker"; @import "../js/components/PopularTopics/PopularTopics"; @import "../js/components/ArticleList/ArticleList"; @import "../js/components/Header/Header"; diff --git a/browser/components/pocket/content/panels/js/components/TagPicker/TagPicker.jsx b/browser/components/pocket/content/panels/js/components/TagPicker/TagPicker.jsx new file mode 100644 index 000000000000..d0a0330cd560 --- /dev/null +++ b/browser/components/pocket/content/panels/js/components/TagPicker/TagPicker.jsx @@ -0,0 +1,77 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import React, { useState } from "react"; + +function TagPicker(props) { + const [tags, setTags] = useState(props.tags); + const [duplicateTag, setDuplicateTag] = useState(null); + + let handleKeyDown = e => { + // Enter tag on comma or enter keypress + if (e.keyCode === 188 || e.keyCode === 13) { + let tag = e.target.value.trim(); + + e.preventDefault(); + e.target.value = ``; // Clear out input + + addTag(tag); + } + }; + + let addTag = tag => { + let newDuplicateTag = tags.find(item => item === tag); + + if (!tag.length) { + return; + } + + if (!newDuplicateTag) { + setTags([...tags, tag]); + } else { + setDuplicateTag(newDuplicateTag); + + setTimeout(() => { + setDuplicateTag(null); + }, 1000); + } + }; + + let removeTag = index => { + let updatedTags = tags.slice(0); // Shallow copied array + updatedTags.splice(index, 1); + setTags(updatedTags); + }; + + return ( +
+

Add Tags:

+
+ {tags.map((tag, i) => ( +
+ {tag} + +
+ ))} + handleKeyDown(e)} + maxlength="25" + > +
+
+ ); +} + +export default TagPicker; diff --git a/browser/components/pocket/content/panels/js/components/TagPicker/TagPicker.scss b/browser/components/pocket/content/panels/js/components/TagPicker/TagPicker.scss new file mode 100644 index 000000000000..af57d8511a87 --- /dev/null +++ b/browser/components/pocket/content/panels/js/components/TagPicker/TagPicker.scss @@ -0,0 +1,44 @@ +.stp_tag_picker { + .stp_tag_picker_tags { + display: flex; + flex-wrap: wrap; + padding: 8px; + border: 1px solid #8F8F9D; + border-radius: 4px; + font-style: normal; + font-weight: normal; + font-size: 1em; + line-height: 1.2em; + color: #15141A; + margin-bottom: 10px; + } + + .stp_tag_picker_tag { + background: #F0F0F4; + border-radius: 4px; + color: #15141A; + display: inline-block; + font-size: 0.7em; + font-style: normal; + line-height: 1em; + font-weight: 600; + margin-bottom: 8px; + margin-inline-end: 8px; + padding: 4px 8px; + transition: background-color 200ms ease-in-out; + } + + .stp_tag_picker_tag_remove { + padding: 5px; + color: #5B5B66; + font-weight: 400; + } + + .stp_tag_picker_tag_duplicate { + background-color: #bbb; + } + + .stp_tag_picker_input { + flex-grow: 1; + } +} \ No newline at end of file diff --git a/browser/components/pocket/content/panels/js/main.bundle.js b/browser/components/pocket/content/panels/js/main.bundle.js index 9f75517be3c5..2c63ae011df7 100644 --- a/browser/components/pocket/content/panels/js/main.bundle.js +++ b/browser/components/pocket/content/panels/js/main.bundle.js @@ -2,7 +2,7 @@ /******/ "use strict"; /******/ var __webpack_modules__ = ({ -/***/ 861: +/***/ 503: /***/ ((__unused_webpack_module, __unused_webpack___webpack_exports__, __webpack_require__) => { @@ -1192,6 +1192,69 @@ SavedOverlay.prototype = { }; /* harmony default export */ const saved_overlay = (SavedOverlay); +;// CONCATENATED MODULE: ./content/panels/js/components/TagPicker/TagPicker.jsx +/* 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/. */ + + +function TagPicker(props) { + const [tags, setTags] = (0,react.useState)(props.tags); + const [duplicateTag, setDuplicateTag] = (0,react.useState)(null); + + let handleKeyDown = e => { + // Enter tag on comma or enter keypress + if (e.keyCode === 188 || e.keyCode === 13) { + let tag = e.target.value.trim(); + e.preventDefault(); + e.target.value = ``; // Clear out input + + addTag(tag); + } + }; + + let addTag = tag => { + let newDuplicateTag = tags.find(item => item === tag); + + if (!tag.length) { + return; + } + + if (!newDuplicateTag) { + setTags([...tags, tag]); + } else { + setDuplicateTag(newDuplicateTag); + setTimeout(() => { + setDuplicateTag(null); + }, 1000); + } + }; + + let removeTag = index => { + let updatedTags = tags.slice(0); // Shallow copied array + + updatedTags.splice(index, 1); + setTags(updatedTags); + }; + + return /*#__PURE__*/react.createElement("div", { + className: "stp_tag_picker" + }, /*#__PURE__*/react.createElement("p", null, "Add Tags:"), /*#__PURE__*/react.createElement("div", { + className: "stp_tag_picker_tags" + }, tags.map((tag, i) => /*#__PURE__*/react.createElement("div", { + className: `stp_tag_picker_tag${duplicateTag === tag ? ` stp_tag_picker_tag_duplicate` : ``}` + }, tag, /*#__PURE__*/react.createElement("button", { + onClick: () => removeTag(i), + className: `stp_tag_picker_tag_remove` + }, "X"))), /*#__PURE__*/react.createElement("input", { + className: "stp_tag_picker_input", + type: "text", + onKeyDown: e => handleKeyDown(e), + maxlength: "25" + }))); +} + +/* harmony default export */ const TagPicker_TagPicker = (TagPicker); ;// CONCATENATED MODULE: ./content/panels/js/style-guide/overlay.js @@ -1200,6 +1263,7 @@ SavedOverlay.prototype = { + var StyleGuideOverlay = function (options) {}; StyleGuideOverlay.prototype = { @@ -1266,6 +1330,10 @@ StyleGuideOverlay.prototype = { url: "https://example.org", alt: "Alt Text" }] + }), /*#__PURE__*/react.createElement("h4", { + className: "stp_styleguide_h4" + }, "TagPicker"), /*#__PURE__*/react.createElement(TagPicker_TagPicker, { + tags: [`futurism`, `politics`, `mozilla`] }), /*#__PURE__*/react.createElement("h3", null, "Typography:"), /*#__PURE__*/react.createElement("h2", { className: "header_large" }, ".header_large"), /*#__PURE__*/react.createElement("h3", { @@ -1524,7 +1592,7 @@ window.pktPanelMessaging = messages; /******/ // startup /******/ // Load entry module and return exports /******/ // This entry module depends on other loaded chunks and execution need to be delayed -/******/ var __webpack_exports__ = __webpack_require__.O(undefined, [736], () => (__webpack_require__(861))) +/******/ var __webpack_exports__ = __webpack_require__.O(undefined, [736], () => (__webpack_require__(503))) /******/ __webpack_exports__ = __webpack_require__.O(__webpack_exports__); /******/ /******/ })() diff --git a/browser/components/pocket/content/panels/js/style-guide/overlay.js b/browser/components/pocket/content/panels/js/style-guide/overlay.js index 8f3abd02f049..370175656f57 100644 --- a/browser/components/pocket/content/panels/js/style-guide/overlay.js +++ b/browser/components/pocket/content/panels/js/style-guide/overlay.js @@ -4,6 +4,7 @@ import Header from "../components/Header/Header"; import ArticleList from "../components/ArticleList/ArticleList"; import Button from "../components/Button/Button"; import PopularTopics from "../components/PopularTopics/PopularTopics"; +import TagPicker from "../components/TagPicker/TagPicker"; var StyleGuideOverlay = function(options) {}; @@ -71,6 +72,8 @@ StyleGuideOverlay.prototype = { }, ]} /> +

TagPicker

+

Typography:

.header_large

.header_medium

diff --git a/browser/components/pocket/content/panels/style-guide.html b/browser/components/pocket/content/panels/style-guide.html index dfc88bbe1a00..6a112d322f9e 100644 --- a/browser/components/pocket/content/panels/style-guide.html +++ b/browser/components/pocket/content/panels/style-guide.html @@ -9,7 +9,7 @@ Pocket: Style Guide - + diff --git a/browser/components/tests/browser/browser.ini b/browser/components/tests/browser/browser.ini index 60d8edc226fb..3f911b60ac2f 100644 --- a/browser/components/tests/browser/browser.ini +++ b/browser/components/tests/browser/browser.ini @@ -5,8 +5,6 @@ support-files = [browser_browserGlue_telemetry.js] [browser_browserGlue_upgradeDialog.js] -skip-if = - os == "linux" && bits = 64 # high frequency intermittent Bug 1744379 [browser_browserGlue_upgradeDialog_trigger.js] [browser_bug538331.js] skip-if = !updater diff --git a/browser/config/mozconfigs/linux64/debug-fuzzing b/browser/config/mozconfigs/linux64/debug-fuzzing index c2ba0f3b01d8..a0e8f39177f0 100644 --- a/browser/config/mozconfigs/linux64/debug-fuzzing +++ b/browser/config/mozconfigs/linux64/debug-fuzzing @@ -2,7 +2,7 @@ ac_add_options --enable-debug . $topsrcdir/build/unix/mozconfig.linux -export LLVM_SYMBOLIZER="$MOZ_FETCHES_DIR/llvm-symbolizer/llvm-symbolizer" +export LLVM_SYMBOLIZER="$MOZ_FETCHES_DIR/llvm-symbolizer/bin/llvm-symbolizer" # Package js shell. export MOZ_PACKAGE_JSSHELL=1 diff --git a/browser/config/mozconfigs/linux64/debug-fuzzing-noopt b/browser/config/mozconfigs/linux64/debug-fuzzing-noopt index 090a041d88bb..2c10d92b100e 100644 --- a/browser/config/mozconfigs/linux64/debug-fuzzing-noopt +++ b/browser/config/mozconfigs/linux64/debug-fuzzing-noopt @@ -2,7 +2,7 @@ ac_add_options --enable-debug . $topsrcdir/build/unix/mozconfig.linux -export LLVM_SYMBOLIZER="$MOZ_FETCHES_DIR/llvm-symbolizer/llvm-symbolizer" +export LLVM_SYMBOLIZER="$MOZ_FETCHES_DIR/llvm-symbolizer/bin/llvm-symbolizer" # Package js shell. export MOZ_PACKAGE_JSSHELL=1 diff --git a/browser/config/mozconfigs/linux64/fuzzing-ccov b/browser/config/mozconfigs/linux64/fuzzing-ccov index f89e908debbd..4cded3867b16 100644 --- a/browser/config/mozconfigs/linux64/fuzzing-ccov +++ b/browser/config/mozconfigs/linux64/fuzzing-ccov @@ -1,6 +1,6 @@ . "$topsrcdir/browser/config/mozconfigs/linux64/code-coverage" -export LLVM_SYMBOLIZER="$MOZ_FETCHES_DIR/llvm-symbolizer/llvm-symbolizer" +export LLVM_SYMBOLIZER="$MOZ_FETCHES_DIR/llvm-symbolizer/bin/llvm-symbolizer" # Even in fuzzing builds without sanitizers, the UBSan runtime is pulled # in as a dependency to allow libFuzzer to have rudimentary stacks. diff --git a/browser/modules/AsyncTabSwitcher.jsm b/browser/modules/AsyncTabSwitcher.jsm index 4cffcd5f812a..50a1c400384e 100644 --- a/browser/modules/AsyncTabSwitcher.jsm +++ b/browser/modules/AsyncTabSwitcher.jsm @@ -12,6 +12,7 @@ const { XPCOMUtils } = ChromeUtils.import( ); XPCOMUtils.defineLazyModuleGetters(this, { AppConstants: "resource://gre/modules/AppConstants.jsm", + PictureInPicture: "resource://gre/modules/PictureInPicture.jsm", Services: "resource://gre/modules/Services.jsm", }); @@ -649,8 +650,7 @@ class AsyncTabSwitcher { let numPending = 0; let numWarming = 0; for (let [tab, state] of this.tabState) { - // Skip print preview browsers since they shouldn't affect tab switching. - if (this.tabbrowser._printPreviewBrowsers.has(tab.linkedBrowser)) { + if (!this.shouldDeactivateDocShell(tab.linkedBrowser)) { continue; } @@ -726,7 +726,7 @@ class AsyncTabSwitcher { // Unload any tabs that can be unloaded. for (let [tab, state] of this.tabState) { - if (this.tabbrowser._printPreviewBrowsers.has(tab.linkedBrowser)) { + if (!this.shouldDeactivateDocShell(tab.linkedBrowser)) { continue; } @@ -852,8 +852,7 @@ class AsyncTabSwitcher { onSizeModeOrOcclusionStateChange() { if (this.minimizedOrFullyOccluded) { for (let [tab, state] of this.tabState) { - // Skip print preview browsers since they shouldn't affect tab switching. - if (this.tabbrowser._printPreviewBrowsers.has(tab.linkedBrowser)) { + if (!this.shouldDeactivateDocShell(tab.linkedBrowser)) { continue; } @@ -915,6 +914,19 @@ class AsyncTabSwitcher { } } + /** + * Check if the browser should be deactivated. If the browser is a print preivew or + * PiP browser then we won't deactive it. + * @param browser The browser to check if it should be deactivated + * @returns false if a print preview or PiP browser else true + */ + shouldDeactivateDocShell(browser) { + return !( + this.tabbrowser._printPreviewBrowsers.has(browser) || + PictureInPicture.isOriginatingBrowser(browser) + ); + } + shouldActivateDocShell(browser) { let tab = this.tabbrowser.getTabForBrowser(browser); let state = this.getTabState(tab); @@ -1220,6 +1232,8 @@ class AsyncTabSwitcher { let linkedBrowser = tab.linkedBrowser; let isActive = linkedBrowser && linkedBrowser.docShellIsActive; let isRendered = linkedBrowser && linkedBrowser.renderLayers; + let isPiP = + linkedBrowser && PictureInPicture.isOriginatingBrowser(linkedBrowser); if (tab === this.lastVisibleTab) { tabString += "V"; @@ -1253,6 +1267,9 @@ class AsyncTabSwitcher { if (isRendered) { extraStates += "R"; } + if (isPiP) { + extraStates += "P"; + } if (extraStates != "") { tabString += `(${extraStates})`; } diff --git a/build/build-clang/clang-13.json b/build/build-clang/clang-13.json index 6f7d9b6921ec..073743eac995 100644 --- a/build/build-clang/clang-13.json +++ b/build/build-clang/clang-13.json @@ -1,6 +1,5 @@ { "patches": [ - "static-llvm-symbolizer_clang_12.patch", "compiler-rt-cross-compile.patch", "find_symbolizer_linux_clang_10.patch", "android-mangling-error_clang_12.patch", diff --git a/build/build-clang/clang-trunk.json b/build/build-clang/clang-trunk.json index 536f64eb93bb..b05667dfff7b 100644 --- a/build/build-clang/clang-trunk.json +++ b/build/build-clang/clang-trunk.json @@ -1,6 +1,5 @@ { "patches": [ - "static-llvm-symbolizer_clang_15.patch", "compiler-rt-cross-compile.patch", "find_symbolizer_linux_clang_15.patch", "android-mangling-error_clang_12.patch", diff --git a/build/build-clang/static-llvm-symbolizer_clang_12.patch b/build/build-clang/static-llvm-symbolizer_clang_12.patch deleted file mode 100644 index b1fe5d145878..000000000000 --- a/build/build-clang/static-llvm-symbolizer_clang_12.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/llvm/tools/llvm-symbolizer/CMakeLists.txt b/llvm/tools/llvm-symbolizer/CMakeLists.txt -index c112e344da7..f0f16f1ba2d 100644 ---- a/llvm/tools/llvm-symbolizer/CMakeLists.txt -+++ b/llvm/tools/llvm-symbolizer/CMakeLists.txt -@@ -18,6 +18,7 @@ set(LLVM_LINK_COMPONENTS - ) - - add_llvm_tool(llvm-symbolizer -+ DISABLE_LLVM_LINK_LLVM_DYLIB - llvm-symbolizer.cpp - DEPENDS - SymbolizerOptsTableGen diff --git a/build/build-clang/static-llvm-symbolizer_clang_15.patch b/build/build-clang/static-llvm-symbolizer_clang_15.patch deleted file mode 100644 index 473df64a4e8e..000000000000 --- a/build/build-clang/static-llvm-symbolizer_clang_15.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/llvm/tools/llvm-symbolizer/CMakeLists.txt b/llvm/tools/llvm-symbolizer/CMakeLists.txt -index f1a6087c0047..faf51c9b5c45 100644 ---- a/llvm/tools/llvm-symbolizer/CMakeLists.txt -+++ b/llvm/tools/llvm-symbolizer/CMakeLists.txt -@@ -18,6 +18,7 @@ set(LLVM_LINK_COMPONENTS - ) - - add_llvm_tool(llvm-symbolizer -+ DISABLE_LLVM_LINK_LLVM_DYLIB - llvm-symbolizer.cpp - - DEPENDS diff --git a/build/unix/mozconfig.asan b/build/unix/mozconfig.asan index e4ec5f6f5776..9c1e7d45e28a 100644 --- a/build/unix/mozconfig.asan +++ b/build/unix/mozconfig.asan @@ -1,6 +1,6 @@ . "$topsrcdir/build/unix/mozconfig.unix" -export LLVM_SYMBOLIZER="$MOZ_FETCHES_DIR/llvm-symbolizer/llvm-symbolizer" +export LLVM_SYMBOLIZER="$MOZ_FETCHES_DIR/llvm-symbolizer/bin/llvm-symbolizer" # # Enable ASan specific code and build workarounds ac_add_options --enable-address-sanitizer diff --git a/build/unix/mozconfig.tsan b/build/unix/mozconfig.tsan index 668f87244e4e..41d742536c0f 100644 --- a/build/unix/mozconfig.tsan +++ b/build/unix/mozconfig.tsan @@ -1,6 +1,6 @@ . "$topsrcdir/build/unix/mozconfig.unix" -export LLVM_SYMBOLIZER="$MOZ_FETCHES_DIR/llvm-symbolizer/llvm-symbolizer" +export LLVM_SYMBOLIZER="$MOZ_FETCHES_DIR/llvm-symbolizer/bin/llvm-symbolizer" # Enable TSan specific code and build workarounds ac_add_options --enable-thread-sanitizer diff --git a/build/win64/mozconfig.asan b/build/win64/mozconfig.asan index 7e27407fd979..e9163671f9bd 100644 --- a/build/win64/mozconfig.asan +++ b/build/win64/mozconfig.asan @@ -7,7 +7,7 @@ if [ -d "$MOZ_FETCHES_DIR/clang" ]; then export LDFLAGS="clang_rt.asan_dynamic-x86_64.lib clang_rt.asan_dynamic_runtime_thunk-x86_64.lib" export MOZ_COPY_PDBS=1 - export LLVM_SYMBOLIZER="$MOZ_FETCHES_DIR/llvm-symbolizer/llvm-symbolizer.exe" + export LLVM_SYMBOLIZER="$MOZ_FETCHES_DIR/llvm-symbolizer/bin/llvm-symbolizer.exe" export MOZ_CLANG_RT_ASAN_LIB_PATH="${CLANG_LIB_DIR}/clang_rt.asan_dynamic-x86_64.dll" fi diff --git a/devtools/server/actors/resources/index.js b/devtools/server/actors/resources/index.js index fccd0383ce67..bdc3783beed2 100644 --- a/devtools/server/actors/resources/index.js +++ b/devtools/server/actors/resources/index.js @@ -31,14 +31,21 @@ const TYPES = { exports.TYPES = TYPES; // Helper dictionaries, which will contain data specific to each resource type. -// - `path` is the absolute path to the module defining the Resource Watcher class, +// - `path` is the absolute path to the module defining the Resource Watcher class. +// // Also see the attributes added by `augmentResourceDictionary` for each type: // - `watchers` is a weak map which will store Resource Watchers // (i.e. devtools/server/actors/resources/ class instances) // keyed by target actor -or- watcher actor. // - `WatcherClass` is a shortcut to the Resource Watcher module. // Each module exports a Resource Watcher class. -// These lists are specific for the parent process and each target type. +// +// These are several dictionaries, which depend how the resource watcher classes are instantiated. + +// Frame target resources are spawned via a BrowsingContext Target Actor. +// Their watcher class receives a target actor as first argument. +// They are instantiated for each observed BrowsingContext, from the content process where it runs. +// They are meant to observe all resources related to a given Browsing Context. const FrameTargetResources = augmentResourceDictionary({ [TYPES.CACHE_STORAGE]: { path: "devtools/server/actors/resources/storage-cache", @@ -92,6 +99,11 @@ const FrameTargetResources = augmentResourceDictionary({ path: "devtools/server/actors/resources/websockets", }, }); + +// Process target resources are spawned via a Process Target Actor. +// Their watcher class receives a process target actor as first argument. +// They are instantiated for each observed Process (parent and all content processes). +// They are meant to observe all resources related to a given process. const ProcessTargetResources = augmentResourceDictionary({ [TYPES.CONSOLE_MESSAGE]: { path: "devtools/server/actors/resources/console-messages", @@ -110,6 +122,11 @@ const ProcessTargetResources = augmentResourceDictionary({ }, }); +// Worker target resources are spawned via a Worker Target Actor. +// Their watcher class receives a worker target actor as first argument. +// They are instantiated for each observed worker, from the worker thread. +// They are meant to observe all resources related to a given worker. +// // We'll only support a few resource types in Workers (console-message, source, // thread state, …) as error and platform messages are not supported since we need access // to Ci, which isn't available in worker context. @@ -126,6 +143,11 @@ const WorkerTargetResources = augmentResourceDictionary({ }, }); +// Parent process resources are spawned via the Watcher Actor. +// Their watcher class receives the watcher actor as first argument. +// They are instantiated once per watcher from the parent process. +// They are meant to observe all resources related to a given context designated by the Watcher (and its sessionContext) +// they should be observed from the parent process. const ParentProcessResources = augmentResourceDictionary({ [TYPES.NETWORK_EVENT]: { path: "devtools/server/actors/resources/network-events", @@ -141,6 +163,15 @@ const ParentProcessResources = augmentResourceDictionary({ }, }); +// Root resources are spawned via the Root Actor. +// Their watcher class receives the root actor as first argument. +// They are instantiated only once from the parent process. +// They are meant to observe anything easily observable from the parent process +// that isn't related to any particular context/target. +// This is especially useful when you need to observe something without having to instantiate a Watcher actor. +const RootResources = augmentResourceDictionary({}); +exports.RootResources = RootResources; + function augmentResourceDictionary(dict) { for (const resource of Object.values(dict)) { resource.watchers = new WeakMap(); @@ -154,15 +185,18 @@ function augmentResourceDictionary(dict) { * For a given actor, return the related dictionary defined just before, * that contains info about how to listen for a given resource type, from a given actor. * - * @param Actor watcherOrTargetActor - * Either a WatcherActor or a TargetActor which can be listening to a resource. + * @param Actor rootOrWatcherOrTargetActor + * Either a RootActor or WatcherActor or a TargetActor which can be listening to a resource. */ -function getResourceTypeDictionary(watcherOrTargetActor) { - const { typeName } = watcherOrTargetActor; +function getResourceTypeDictionary(rootOrWatcherOrTargetActor) { + const { typeName } = rootOrWatcherOrTargetActor; + if (typeName == "root") { + return RootResources; + } if (typeName == "watcher") { return ParentProcessResources; } - const { targetType } = watcherOrTargetActor; + const { targetType } = rootOrWatcherOrTargetActor; return getResourceTypeDictionaryForTargetType(targetType); } @@ -189,16 +223,16 @@ function getResourceTypeDictionaryForTargetType(targetType) { * For a given actor, return the object stored in one of the previous dictionary * that contains info about how to listen for a given resource type, from a given actor. * - * @param Actor watcherOrTargetActor - * Either a WatcherActor or a TargetActor which can be listening to a resource. + * @param Actor rootOrWatcherOrTargetActor + * Either a RootActor or WatcherActor or a TargetActor which can be listening to a resource. * @param String resourceType * The resource type to be observed. */ -function getResourceTypeEntry(watcherOrTargetActor, resourceType) { - const dict = getResourceTypeDictionary(watcherOrTargetActor); +function getResourceTypeEntry(rootOrWatcherOrTargetActor, resourceType) { + const dict = getResourceTypeDictionary(rootOrWatcherOrTargetActor); if (!(resourceType in dict)) { throw new Error( - `Unsupported resource type '${resourceType}' for ${watcherOrTargetActor.typeName}` + `Unsupported resource type '${resourceType}' for ${rootOrWatcherOrTargetActor.typeName}` ); } return dict[resourceType]; @@ -208,43 +242,49 @@ function getResourceTypeEntry(watcherOrTargetActor, resourceType) { * Start watching for a new list of resource types. * This will also emit all already existing resources before resolving. * - * @param Actor watcherOrTargetActor - * Either a WatcherActor or a TargetActor which can be listening to a resource. - * WatcherActor will be used for resources listened from the parent process, - * and TargetActor will be used for resources listened from the content process. + * @param Actor rootOrWatcherOrTargetActor + * Either a RootActor or WatcherActor or a TargetActor which can be listening to a resource: + * * RootActor will be used for resources observed from the parent process and aren't related to any particular + * context/descriptor. They can be observed right away when connecting to the RDP server + * without instantiating any actor other than the root actor. + * * WatcherActor will be used for resources listened from the parent process. + * * TargetActor will be used for resources listened from the content process. * This actor: * - defines what context to observe (browsing context, process, worker, ...) * Via browsingContextID, windows, docShells attributes for the target actor. * Via the `sessionContext` object for the watcher actor. + * (only for Watcher and Target actors. Root actor is context-less.) * - exposes `notifyResourceAvailable` method to be notified about the available resources * @param Array resourceTypes * List of all type of resource to listen to. */ -async function watchResources(watcherOrTargetActor, resourceTypes) { +async function watchResources(rootOrWatcherOrTargetActor, resourceTypes) { // If we are given a target actor, filter out the resource types supported by the target. // When using sharedData to pass types between processes, we are passing them for all target types. - const { targetType } = watcherOrTargetActor; + const { targetType } = rootOrWatcherOrTargetActor; + // Only target actors usecase will have a target type. + // For Root and Watcher we process the `resourceTypes` list unfiltered. if (targetType) { resourceTypes = getResourceTypesForTargetType(resourceTypes, targetType); } for (const resourceType of resourceTypes) { const { watchers, WatcherClass } = getResourceTypeEntry( - watcherOrTargetActor, + rootOrWatcherOrTargetActor, resourceType ); // Ignore resources we're already listening to - if (watchers.has(watcherOrTargetActor)) { + if (watchers.has(rootOrWatcherOrTargetActor)) { continue; } const watcher = new WatcherClass(); - await watcher.watch(watcherOrTargetActor, { - onAvailable: watcherOrTargetActor.notifyResourceAvailable, - onDestroyed: watcherOrTargetActor.notifyResourceDestroyed, - onUpdated: watcherOrTargetActor.notifyResourceUpdated, + await watcher.watch(rootOrWatcherOrTargetActor, { + onAvailable: rootOrWatcherOrTargetActor.notifyResourceAvailable, + onDestroyed: rootOrWatcherOrTargetActor.notifyResourceDestroyed, + onUpdated: rootOrWatcherOrTargetActor.notifyResourceUpdated, }); - watchers.set(watcherOrTargetActor, watcher); + watchers.set(rootOrWatcherOrTargetActor, watcher); } } exports.watchResources = watchResources; @@ -276,23 +316,23 @@ exports.hasResourceTypesForTargets = hasResourceTypesForTargets; /** * Stop watching for a list of resource types. * - * @param Actor watcherOrTargetActor + * @param Actor rootOrWatcherOrTargetActor * The related actor, already passed to watchResources. * @param Array resourceTypes * List of all type of resource to stop listening to. */ -function unwatchResources(watcherOrTargetActor, resourceTypes) { +function unwatchResources(rootOrWatcherOrTargetActor, resourceTypes) { for (const resourceType of resourceTypes) { // Pull all info about this resource type from `Resources` global object const { watchers } = getResourceTypeEntry( - watcherOrTargetActor, + rootOrWatcherOrTargetActor, resourceType ); - const watcher = watchers.get(watcherOrTargetActor); + const watcher = watchers.get(rootOrWatcherOrTargetActor); if (watcher) { watcher.destroy(); - watchers.delete(watcherOrTargetActor); + watchers.delete(rootOrWatcherOrTargetActor); } } } @@ -301,21 +341,21 @@ exports.unwatchResources = unwatchResources; /** * Stop watching for all watched resources on a given actor. * - * @param Actor watcherOrTargetActor + * @param Actor rootOrWatcherOrTargetActor * The related actor, already passed to watchResources. */ -function unwatchAllTargetResources(watcherOrTargetActor) { +function unwatchAllResources(rootOrWatcherOrTargetActor) { for (const { watchers } of Object.values( - getResourceTypeDictionary(watcherOrTargetActor) + getResourceTypeDictionary(rootOrWatcherOrTargetActor) )) { - const watcher = watchers.get(watcherOrTargetActor); + const watcher = watchers.get(rootOrWatcherOrTargetActor); if (watcher) { watcher.destroy(); - watchers.delete(watcherOrTargetActor); + watchers.delete(rootOrWatcherOrTargetActor); } } } -exports.unwatchAllTargetResources = unwatchAllTargetResources; +exports.unwatchAllResources = unwatchAllResources; /** * If we are watching for the given resource type, diff --git a/devtools/server/actors/root.js b/devtools/server/actors/root.js index 175aeb8d8c58..deb8e4245a04 100644 --- a/devtools/server/actors/root.js +++ b/devtools/server/actors/root.js @@ -18,6 +18,7 @@ const { const { DevToolsServer } = require("devtools/server/devtools-server"); const protocol = require("devtools/shared/protocol"); const { rootSpec } = require("devtools/shared/specs/root"); +const Resources = require("devtools/server/actors/resources/index"); loader.lazyRequireGetter( this, @@ -112,14 +113,28 @@ exports.RootActor = protocol.ActorClassWithSpec(rootSpec, { this ); this._onProcessListChanged = this.onProcessListChanged.bind(this); + this.notifyResourceAvailable = this.notifyResourceAvailable.bind(this); + this.notifyResourceDestroyed = this.notifyResourceDestroyed.bind(this); + this._extraActors = {}; this._globalActorPool = new LazyPool(this.conn); this.applicationType = "browser"; + // Compute the list of all supported Root Resources + const supportedResources = {}; + for (const resourceType in Resources.RootResources) { + supportedResources[resourceType] = true; + } + this.traits = { networkMonitor: true, + + // @backward-compat { version 100 } Expose the supported resources. + // This traits should be kept, but we can later remove the backward compat comment. + resources: supportedResources, + // @backward-compat { version 84 } Expose the pref value to the client. // Services.prefs is undefined in xpcshell tests. workerConsoleApiMessagesDispatchedToMainThread: Services.prefs @@ -163,6 +178,8 @@ exports.RootActor = protocol.ActorClassWithSpec(rootSpec, { * Destroys the actor from the browser window. */ destroy: function() { + Resources.unwatchAllResources(this); + protocol.Actor.prototype.destroy.call(this); /* Tell the live lists we aren't watching any more. */ @@ -540,6 +557,52 @@ exports.RootActor = protocol.ActorClassWithSpec(rootSpec, { delete this._extraActors[name]; } }, + + /** + * Start watching for a list of resource types. + * + * See WatcherActor.watchResources. + */ + async watchResources(resourceTypes) { + await Resources.watchResources(this, resourceTypes); + }, + + /** + * Stop watching for a list of resource types. + * + * See WatcherActor.unwatchResources. + */ + unwatchResources(resourceTypes) { + Resources.unwatchResources( + this, + Resources.getParentProcessResourceTypes(resourceTypes) + ); + }, + /** + * Called by Resource Watchers, when new resources are available. + * + * @param Array resources + * List of all available resources. A resource is a JSON object piped over to the client. + * It may contain actor IDs, actor forms, to be manually marshalled by the client. + */ + notifyResourceAvailable(resources) { + this._emitResourcesForm("resource-available-form", resources); + }, + + notifyResourceDestroyed(resources) { + this._emitResourcesForm("resource-destroyed-form", resources); + }, + + /** + * Wrapper around emit for resource forms. + */ + _emitResourcesForm(name, resources) { + if (resources.length === 0) { + // Don't try to emit if the resources array is empty. + return; + } + this.emit(name, resources); + }, }); /** diff --git a/devtools/server/actors/targets/content-process.js b/devtools/server/actors/targets/content-process.js index 12b5872a1445..d0e75e938eae 100644 --- a/devtools/server/actors/targets/content-process.js +++ b/devtools/server/actors/targets/content-process.js @@ -213,7 +213,7 @@ const ContentProcessTargetActor = TargetActorMixin( if (this.isDestroyed()) { return; } - Resources.unwatchAllTargetResources(this); + Resources.unwatchAllResources(this); Actor.prototype.destroy.call(this); diff --git a/devtools/server/actors/targets/window-global.js b/devtools/server/actors/targets/window-global.js index 849fc3ae4481..532d9fd4248b 100644 --- a/devtools/server/actors/targets/window-global.js +++ b/devtools/server/actors/targets/window-global.js @@ -727,7 +727,7 @@ const windowGlobalTargetPrototype = { Actor.prototype.destroy.call(this); TargetActorRegistry.unregisterTargetActor(this); - Resources.unwatchAllTargetResources(this); + Resources.unwatchAllResources(this); }, /** diff --git a/devtools/shared/commands/index.js b/devtools/shared/commands/index.js index 1838d5d89762..8af484230842 100644 --- a/devtools/shared/commands/index.js +++ b/devtools/shared/commands/index.js @@ -13,6 +13,8 @@ const Commands = { "devtools/shared/commands/inspected-window/inspected-window-command", inspectorCommand: "devtools/shared/commands/inspector/inspector-command", resourceCommand: "devtools/shared/commands/resource/resource-command", + rootResourceCommand: + "devtools/shared/commands/root-resource/root-resource-command", scriptCommand: "devtools/shared/commands/script/script-command", targetCommand: "devtools/shared/commands/target/target-command", targetConfigurationCommand: diff --git a/devtools/shared/commands/moz.build b/devtools/shared/commands/moz.build index 16c5bfcbf9ed..36bacf281d58 100644 --- a/devtools/shared/commands/moz.build +++ b/devtools/shared/commands/moz.build @@ -6,6 +6,7 @@ DIRS += [ "inspected-window", "inspector", "resource", + "root-resource", "script", "target", "target-configuration", diff --git a/devtools/shared/commands/root-resource/moz.build b/devtools/shared/commands/root-resource/moz.build new file mode 100644 index 000000000000..2bf7204d1f9d --- /dev/null +++ b/devtools/shared/commands/root-resource/moz.build @@ -0,0 +1,7 @@ +# 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/. + +DevToolsModules( + "root-resource-command.js", +) diff --git a/devtools/shared/commands/root-resource/root-resource-command.js b/devtools/shared/commands/root-resource/root-resource-command.js new file mode 100644 index 000000000000..c9934c9ecb46 --- /dev/null +++ b/devtools/shared/commands/root-resource/root-resource-command.js @@ -0,0 +1,330 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const { throttle } = require("devtools/shared/throttle"); + +class RootResourceCommand { + /** + * This class helps retrieving existing and listening to "root" resources. + * + * This is a fork of ResourceCommand, but specific to context-less + * resources which can be listened to right away when connecting to the RDP server. + * + * The main difference in term of implementation is that: + * - we receive a root front as constructor argument (instead of `commands` object) + * - we only listen for RDP events on the Root actor (instead of watcher and target actors) + * - there is no legacy listener support + * - there is no resource transformers + * - there is a lot of logic around targets that is removed here. + * + * See ResourceCommand for comments and jsdoc. + * + * TODO Bug 1758530 - Investigate sharing code with ResourceCommand instead of forking. + * + * @param object commands + * The commands object with all interfaces defined from devtools/shared/commands/ + * @param object rootFront + * Front for the Root actor. + */ + constructor({ commands, rootFront }) { + this.rootFront = rootFront ? rootFront : commands.client.mainRoot; + + this._onResourceAvailable = this._onResourceAvailable.bind(this); + this._onResourceDestroyed = this._onResourceDestroyed.bind(this); + + this._watchers = []; + + this._pendingWatchers = new Set(); + + this._cache = []; + this._listenedResources = new Set(); + + this._processingExistingResources = new Set(); + + this._notifyWatchers = this._notifyWatchers.bind(this); + this._throttledNotifyWatchers = throttle(this._notifyWatchers, 100); + } + + getAllResources(resourceType) { + return this._cache.filter(r => r.resourceType === resourceType); + } + + getResourceById(resourceType, resourceId) { + return this._cache.find( + r => r.resourceType === resourceType && r.resourceId === resourceId + ); + } + + async watchResources(resources, options) { + const { + onAvailable, + onUpdated, + onDestroyed, + ignoreExistingResources = false, + } = options; + + if (typeof onAvailable !== "function") { + throw new Error( + "RootResourceCommand.watchResources expects an onAvailable function as argument" + ); + } + + for (const type of resources) { + if (!this._isValidResourceType(type)) { + throw new Error( + `RootResourceCommand.watchResources invoked with an unknown type: "${type}"` + ); + } + } + + const pendingWatcher = { + resources, + onAvailable, + }; + this._pendingWatchers.add(pendingWatcher); + + if (!this._listenerRegistered) { + this._listenerRegistered = true; + this.rootFront.on("resource-available-form", this._onResourceAvailable); + this.rootFront.on("resource-destroyed-form", this._onResourceDestroyed); + } + + const promises = []; + for (const resource of resources) { + promises.push(this._startListening(resource)); + } + await Promise.all(promises); + + this._notifyWatchers(); + + this._pendingWatchers.delete(pendingWatcher); + + const watchedResources = pendingWatcher.resources; + + if (!watchedResources.length) { + return; + } + + this._watchers.push({ + resources: watchedResources, + onAvailable, + onUpdated, + onDestroyed, + pendingEvents: [], + }); + + if (!ignoreExistingResources) { + await this._forwardExistingResources(watchedResources, onAvailable); + } + } + + unwatchResources(resources, options) { + const { onAvailable } = options; + + if (typeof onAvailable !== "function") { + throw new Error( + "RootResourceCommand.unwatchResources expects an onAvailable function as argument" + ); + } + + for (const type of resources) { + if (!this._isValidResourceType(type)) { + throw new Error( + `RootResourceCommand.unwatchResources invoked with an unknown type: "${type}"` + ); + } + } + + const allWatchers = [...this._watchers, ...this._pendingWatchers]; + for (const watcherEntry of allWatchers) { + if (watcherEntry.onAvailable == onAvailable) { + watcherEntry.resources = watcherEntry.resources.filter(resourceType => { + return !resources.includes(resourceType); + }); + } + } + this._watchers = this._watchers.filter(entry => { + return entry.resources.length > 0; + }); + + for (const resource of resources) { + const isResourceWatched = allWatchers.some(watcherEntry => + watcherEntry.resources.includes(resource) + ); + + if (!isResourceWatched && this._listenedResources.has(resource)) { + this._stopListening(resource); + } + } + } + + async waitForNextResource( + resourceType, + { ignoreExistingResources = false, predicate } = {} + ) { + predicate = predicate || (resource => !!resource); + + let resolve; + const promise = new Promise(r => (resolve = r)); + const onAvailable = async resources => { + const matchingResource = resources.find(resource => predicate(resource)); + if (matchingResource) { + this.unwatchResources([resourceType], { onAvailable }); + resolve(matchingResource); + } + }; + + await this.watchResources([resourceType], { + ignoreExistingResources, + onAvailable, + }); + return { onResource: promise }; + } + + async _onResourceAvailable(resources) { + for (const resource of resources) { + const { resourceType } = resource; + + resource.isAlreadyExistingResource = this._processingExistingResources.has( + resourceType + ); + + this._queueResourceEvent("available", resourceType, resource); + + this._cache.push(resource); + } + + this._throttledNotifyWatchers(); + } + + async _onResourceDestroyed(resources) { + for (const resource of resources) { + const { resourceType, resourceId } = resource; + + let index = -1; + if (resourceId) { + index = this._cache.findIndex( + cachedResource => + cachedResource.resourceType == resourceType && + cachedResource.resourceId == resourceId + ); + } else { + index = this._cache.indexOf(resource); + } + if (index >= 0) { + this._cache.splice(index, 1); + } else { + console.warn( + `Resource ${resourceId || ""} of ${resourceType} was not found.` + ); + } + + this._queueResourceEvent("destroyed", resourceType, resource); + } + this._throttledNotifyWatchers(); + } + + _queueResourceEvent(callbackType, resourceType, update) { + for (const { resources, pendingEvents } of this._watchers) { + if (!resources.includes(resourceType)) { + continue; + } + if (pendingEvents.length > 0) { + const lastEvent = pendingEvents[pendingEvents.length - 1]; + if (lastEvent.callbackType == callbackType) { + lastEvent.updates.push(update); + continue; + } + } + pendingEvents.push({ + callbackType, + updates: [update], + }); + } + } + + _notifyWatchers() { + for (const watcherEntry of this._watchers) { + const { onAvailable, onDestroyed, pendingEvents } = watcherEntry; + watcherEntry.pendingEvents = []; + + for (const { callbackType, updates } of pendingEvents) { + try { + if (callbackType == "available") { + onAvailable(updates, { areExistingResources: false }); + } else if (callbackType == "destroyed" && onDestroyed) { + onDestroyed(updates); + } + } catch (e) { + console.error( + "Exception while calling a RootResourceCommand", + callbackType, + "callback", + ":", + e + ); + } + } + } + } + + _isValidResourceType(type) { + return this.ALL_TYPES.includes(type); + } + + async _startListening(resourceType) { + if (this._listenedResources.has(resourceType)) { + return; + } + this._listenedResources.add(resourceType); + + this._processingExistingResources.add(resourceType); + + // For now, if the server doesn't support the resource type + // act as if we were listening, but do nothing. + // Calling watchResources/unwatchResources will work fine, + // but no resource will be notified. + if (this.rootFront.traits.resources?.[resourceType]) { + await this.rootFront.watchResources([resourceType]); + } + this._processingExistingResources.delete(resourceType); + } + + async _forwardExistingResources(resourceTypes, onAvailable) { + const existingResources = this._cache.filter(resource => + resourceTypes.includes(resource.resourceType) + ); + if (existingResources.length > 0) { + await onAvailable(existingResources, { areExistingResources: true }); + } + } + + _stopListening(resourceType) { + if (!this._listenedResources.has(resourceType)) { + throw new Error( + `Stopped listening for resource '${resourceType}' that isn't being listened to` + ); + } + this._listenedResources.delete(resourceType); + + this._cache = this._cache.filter( + cachedResource => cachedResource.resourceType !== resourceType + ); + + if ( + !this.rootFront.isDestroyed() && + this.rootFront.traits.resources?.[resourceType] + ) { + this.rootFront.unwatchResources([resourceType]); + } + } +} + +RootResourceCommand.TYPES = RootResourceCommand.prototype.TYPES = {}; +RootResourceCommand.ALL_TYPES = RootResourceCommand.prototype.ALL_TYPES = Object.values( + RootResourceCommand.TYPES +); +module.exports = RootResourceCommand; diff --git a/devtools/shared/specs/root.js b/devtools/shared/specs/root.js index aa2d6607a808..f1b1c5e7d9a7 100644 --- a/devtools/shared/specs/root.js +++ b/devtools/shared/specs/root.js @@ -80,6 +80,20 @@ const rootSpecPrototype = { }, }, + watchResources: { + request: { + resourceTypes: Arg(0, "array:string"), + }, + response: {}, + }, + + unwatchResources: { + request: { + resourceTypes: Arg(0, "array:string"), + }, + oneway: true, + }, + requestTypes: { request: {}, response: RetVal("json"), @@ -105,6 +119,15 @@ const rootSpecPrototype = { processListChanged: { type: "processListChanged", }, + + "resource-available-form": { + type: "resource-available-form", + resources: Arg(0, "array:json"), + }, + "resource-destroyed-form": { + type: "resource-destroyed-form", + resources: Arg(0, "array:json"), + }, }, }; diff --git a/dom/base/RemoteOuterWindowProxy.cpp b/dom/base/RemoteOuterWindowProxy.cpp index 6c8738f2b39e..aa561c7e6ddf 100644 --- a/dom/base/RemoteOuterWindowProxy.cpp +++ b/dom/base/RemoteOuterWindowProxy.cpp @@ -8,6 +8,7 @@ #include "js/Proxy.h" #include "mozilla/Maybe.h" #include "mozilla/dom/BrowsingContext.h" +#include "mozilla/dom/ProxyHandlerUtils.h" #include "mozilla/dom/RemoteObjectProxy.h" #include "mozilla/dom/WindowBinding.h" #include "mozilla/dom/WindowProxyHolder.h" diff --git a/dom/base/Selection.cpp b/dom/base/Selection.cpp index f74341ceee48..db6c605960b7 100644 --- a/dom/base/Selection.cpp +++ b/dom/base/Selection.cpp @@ -97,12 +97,12 @@ static constexpr nsLiteralCString kNoDocumentTypeNodeError = static constexpr nsLiteralCString kNoRangeExistsError = "No selection range exists"_ns; +namespace mozilla { + /****************************************************************************** * Utility methods defined in nsISelectionController.idl ******************************************************************************/ -namespace mozilla { - const char* ToChar(SelectionType aSelectionType) { switch (aSelectionType) { case SelectionType::eInvalid: @@ -134,6 +134,48 @@ const char* ToChar(SelectionType aSelectionType) { } } +/****************************************************************************** + * Utility methods defined in nsISelectionListener.idl + ******************************************************************************/ + +nsCString SelectionChangeReasonsToCString(int16_t aReasons) { + nsCString reasons; + if (!aReasons) { + reasons.AssignLiteral("NO_REASON"); + return reasons; + } + auto EnsureSeparator = [](nsCString& aString) -> void { + if (!aString.IsEmpty()) { + aString.AppendLiteral(" | "); + } + }; + struct ReasonData { + int16_t mReason; + const char* mReasonStr; + + ReasonData(int16_t aReason, const char* aReasonStr) + : mReason(aReason), mReasonStr(aReasonStr) {} + }; + for (const ReasonData& reason : + {ReasonData(nsISelectionListener::DRAG_REASON, "DRAG_REASON"), + ReasonData(nsISelectionListener::MOUSEDOWN_REASON, "MOUSEDOWN_REASON"), + ReasonData(nsISelectionListener::MOUSEUP_REASON, "MOUSEUP_REASON"), + ReasonData(nsISelectionListener::KEYPRESS_REASON, "KEYPRESS_REASON"), + ReasonData(nsISelectionListener::SELECTALL_REASON, "SELECTALL_REASON"), + ReasonData(nsISelectionListener::COLLAPSETOSTART_REASON, + "COLLAPSETOSTART_REASON"), + ReasonData(nsISelectionListener::COLLAPSETOEND_REASON, + "COLLAPSETOEND_REASON"), + ReasonData(nsISelectionListener::IME_REASON, "IME_REASON"), + ReasonData(nsISelectionListener::JS_REASON, "JS_REASON")}) { + if (aReasons & reason.mReason) { + EnsureSeparator(reasons); + reasons.Append(reason.mReasonStr); + } + } + return reasons; +} + } // namespace mozilla //#define DEBUG_SELECTION // uncomment for printf describing every collapse and @@ -3180,17 +3222,17 @@ void Selection::NotifySelectionListeners() { } } -void Selection::StartBatchChanges() { +void Selection::StartBatchChanges(const char* aDetails) { if (mFrameSelection) { RefPtr frameSelection = mFrameSelection; - frameSelection->StartBatchChanges(); + frameSelection->StartBatchChanges(aDetails); } } -void Selection::EndBatchChanges(int16_t aReason) { +void Selection::EndBatchChanges(const char* aDetails, int16_t aReasons) { if (mFrameSelection) { RefPtr frameSelection = mFrameSelection; - frameSelection->EndBatchChanges(aReason); + frameSelection->EndBatchChanges(aDetails, aReasons); } } @@ -3403,7 +3445,7 @@ void Selection::SetBaseAndExtentInternal(InLimiter aInLimiter, // after we set the direction. // XXX If they are disconnected, shouldn't we return error before allocating // new nsRange instance? - SelectionBatcher batch(this); + SelectionBatcher batch(this, __FUNCTION__); const Maybe order = nsContentUtils::ComparePoints(aAnchorRef, aFocusRef); if (order && (*order <= 0)) { @@ -3442,7 +3484,7 @@ void Selection::SetStartAndEndInternal(InLimiter aInLimiter, } // Don't fire "selectionchange" event until everything done. - SelectionBatcher batch(this); + SelectionBatcher batch(this, __FUNCTION__); if (aInLimiter == InLimiter::eYes) { if (!mFrameSelection || diff --git a/dom/base/Selection.h b/dom/base/Selection.h index 38904d167752..f2033ed49c15 100644 --- a/dom/base/Selection.h +++ b/dom/base/Selection.h @@ -74,12 +74,25 @@ class Selection final : public nsSupportsWeakReference, NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Selection) - // match this up with EndbatchChanges. will stop ui updates while multiple - // selection methods are called - void StartBatchChanges(); + /** + * Match this up with EndbatchChanges. will stop ui updates while multiple + * selection methods are called + * + * @param aDetails string to explian why this is called. This won't be + * stored nor exposed to selection listeners etc. Just for logging. + */ + void StartBatchChanges(const char* aDetails); - // match this up with StartBatchChanges - void EndBatchChanges(int16_t aReason = nsISelectionListener::NO_REASON); + /** + * Match this up with StartBatchChanges + * + * @param aDetails string to explian why this is called. This won't be + * stored nor exposed to selection listeners etc. Just for logging. + * @param aReasons potentially multiple of the reasons defined in + * nsISelectionListener.idl + */ + void EndBatchChanges(const char* aDetails, + int16_t aReason = nsISelectionListener::NO_REASON); /** * NotifyAutoCopy() starts to notify AutoCopyListener of selection changes. @@ -938,24 +951,36 @@ class Selection final : public nsSupportsWeakReference, // Stack-class to turn on/off selection batching. class MOZ_STACK_CLASS SelectionBatcher final { private: - RefPtr mSelection; - int16_t mReason; + const RefPtr mSelection; + const int16_t mReasons; + const char* const mRequesterFuncName; public: - explicit SelectionBatcher(Selection& aSelectionRef) - : SelectionBatcher(&aSelectionRef) {} + /** + * @param aRequesterFuncName function name which wants the selection batch. + * This won't be stored nor exposed to selection listeners etc, used only for + * logging. This MUST be living when the destructor runs. + */ + // TODO: Mark these constructors `MOZ_CAN_RUN_SCRIPT` because the destructor + // may run script via nsISelectionListener. + explicit SelectionBatcher(Selection& aSelectionRef, + const char* aRequesterFuncName, + int16_t aReasons = nsISelectionListener::NO_REASON) + : SelectionBatcher(&aSelectionRef, aRequesterFuncName, aReasons) {} explicit SelectionBatcher(Selection* aSelection, - int16_t aReason = nsISelectionListener::NO_REASON) { - mSelection = aSelection; - mReason = aReason; + const char* aRequesterFuncName, + int16_t aReasons = nsISelectionListener::NO_REASON) + : mSelection(aSelection), + mReasons(aReasons), + mRequesterFuncName(aRequesterFuncName) { if (mSelection) { - mSelection->StartBatchChanges(); + mSelection->StartBatchChanges(mRequesterFuncName); } } ~SelectionBatcher() { if (mSelection) { - mSelection->EndBatchChanges(mReason); + mSelection->EndBatchChanges(mRequesterFuncName, mReasons); } } }; diff --git a/dom/base/WindowNamedPropertiesHandler.cpp b/dom/base/WindowNamedPropertiesHandler.cpp index 03b6343dcb97..7601438af6b5 100644 --- a/dom/base/WindowNamedPropertiesHandler.cpp +++ b/dom/base/WindowNamedPropertiesHandler.cpp @@ -6,6 +6,7 @@ #include "WindowNamedPropertiesHandler.h" #include "mozilla/dom/EventTargetBinding.h" +#include "mozilla/dom/ProxyHandlerUtils.h" #include "mozilla/dom/WindowBinding.h" #include "mozilla/dom/WindowProxyHolder.h" #include "nsContentUtils.h" diff --git a/dom/base/nsGlobalWindowInner.cpp b/dom/base/nsGlobalWindowInner.cpp index c5700544ed53..48974006615c 100644 --- a/dom/base/nsGlobalWindowInner.cpp +++ b/dom/base/nsGlobalWindowInner.cpp @@ -111,7 +111,6 @@ #include "mozilla/dom/ContentFrameMessageManager.h" #include "mozilla/dom/ContentMediaController.h" #include "mozilla/dom/CustomElementRegistry.h" -#include "mozilla/dom/DOMJSProxyHandler.h" #include "mozilla/dom/DebuggerNotification.h" #include "mozilla/dom/DebuggerNotificationBinding.h" #include "mozilla/dom/DebuggerNotificationManager.h" @@ -151,6 +150,7 @@ #include "mozilla/dom/PopupBlocker.h" #include "mozilla/dom/PrimitiveConversions.h" #include "mozilla/dom/Promise.h" +#include "mozilla/dom/ProxyHandlerUtils.h" #include "mozilla/dom/RootedDictionary.h" #include "mozilla/dom/ScriptLoader.h" #include "mozilla/dom/ScriptSettings.h" diff --git a/dom/base/nsGlobalWindowOuter.cpp b/dom/base/nsGlobalWindowOuter.cpp index 6b3602c736e6..25253594da31 100644 --- a/dom/base/nsGlobalWindowOuter.cpp +++ b/dom/base/nsGlobalWindowOuter.cpp @@ -39,6 +39,7 @@ #include "mozilla/dom/Storage.h" #include "mozilla/dom/MaybeCrossOriginObject.h" #include "mozilla/dom/Performance.h" +#include "mozilla/dom/ProxyHandlerUtils.h" #include "mozilla/dom/StorageEvent.h" #include "mozilla/dom/StorageEventBinding.h" #include "mozilla/dom/StorageNotifierService.h" diff --git a/dom/base/nsISelectionListener.idl b/dom/base/nsISelectionListener.idl index 4aed15d1a0f0..ed195145c63f 100644 --- a/dom/base/nsISelectionListener.idl +++ b/dom/base/nsISelectionListener.idl @@ -29,4 +29,13 @@ interface nsISelectionListener : nsISupports in short reason); }; +%{C++ +namespace mozilla { +/** + * Returning names of `nsISelectionListener::*_REASON` in aReasons. + */ +nsCString SelectionChangeReasonsToCString(int16_t aReasons); + +} // namespace mozilla +%} diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index cab10064d034..46dbda52bf27 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -65,6 +65,7 @@ #include "mozilla/dom/HTMLElementBinding.h" #include "mozilla/dom/HTMLEmbedElementBinding.h" #include "mozilla/dom/MaybeCrossOriginObject.h" +#include "mozilla/dom/ObservableArrayProxyHandler.h" #include "mozilla/dom/ReportingUtils.h" #include "mozilla/dom/XULElementBinding.h" #include "mozilla/dom/XULFrameElementBinding.h" @@ -3505,11 +3506,11 @@ nsresult UnwrapWindowProxyArg(JSContext* cx, JS::Handle src, return NS_OK; } -template -bool GetMaplikeSetlikeBackingObject(JSContext* aCx, JS::Handle aObj, - size_t aSlotIndex, - JS::MutableHandle aBackingObj, - bool* aBackingObjCreated) { +template +static bool GetBackingObject(JSContext* aCx, JS::Handle aObj, + size_t aSlotIndex, + JS::MutableHandle aBackingObj, + bool* aBackingObjCreated, Args... aArgs) { JS::Rooted reflector(aCx); reflector = IsDOMObject(aObj) ? aObj @@ -3526,7 +3527,7 @@ bool GetMaplikeSetlikeBackingObject(JSContext* aCx, JS::Handle aObj, { JSAutoRealm ar(aCx, reflector); JS::Rooted newBackingObj(aCx); - newBackingObj.set(Method(aCx)); + newBackingObj.set(Method(aCx, aArgs...)); if (NS_WARN_IF(!newBackingObj)) { return false; } @@ -3549,16 +3550,35 @@ bool GetMaplikeBackingObject(JSContext* aCx, JS::Handle aObj, size_t aSlotIndex, JS::MutableHandle aBackingObj, bool* aBackingObjCreated) { - return GetMaplikeSetlikeBackingObject( - aCx, aObj, aSlotIndex, aBackingObj, aBackingObjCreated); + return GetBackingObject(aCx, aObj, aSlotIndex, aBackingObj, + aBackingObjCreated); } bool GetSetlikeBackingObject(JSContext* aCx, JS::Handle aObj, size_t aSlotIndex, JS::MutableHandle aBackingObj, bool* aBackingObjCreated) { - return GetMaplikeSetlikeBackingObject( - aCx, aObj, aSlotIndex, aBackingObj, aBackingObjCreated); + return GetBackingObject(aCx, aObj, aSlotIndex, aBackingObj, + aBackingObjCreated); +} + +static inline JSObject* NewObservableArrayProxyObject( + JSContext* aCx, const ObservableArrayProxyHandler* aHandler) { + JS::RootedObject target(aCx, JS::NewArrayObject(aCx, 0)); + if (NS_WARN_IF(!target)) { + return nullptr; + } + + JS::RootedValue targetValue(aCx, JS::ObjectValue(*target)); + return js::NewProxyObject(aCx, aHandler, targetValue, nullptr); +} + +bool GetObservableArrayBackingObject( + JSContext* aCx, JS::Handle aObj, size_t aSlotIndex, + JS::MutableHandle aBackingObj, bool* aBackingObjCreated, + const ObservableArrayProxyHandler* aHandler) { + return GetBackingObject( + aCx, aObj, aSlotIndex, aBackingObj, aBackingObjCreated, aHandler); } bool ForEachHandler(JSContext* aCx, unsigned aArgc, JS::Value* aVp) { diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index e1064fb9afd2..4f48889d1d72 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -62,6 +62,7 @@ class CustomElementReactionsStack; class Document; class EventTarget; class MessageManagerGlobal; +class ObservableArrayProxyHandler; class DedicatedWorkerGlobalScope; template class Record; @@ -370,6 +371,13 @@ MOZ_ALWAYS_INLINE nsresult UnwrapObject(JSObject* obj, OwningNonNull& value, obj, value, PrototypeID, PrototypeTraits::Depth, cx); } +template +MOZ_ALWAYS_INLINE nsresult UnwrapObject(JSObject* obj, NonNull& value, + const CxType& cx) { + return binding_detail::UnwrapObjectInternal( + obj, value, PrototypeID, PrototypeTraits::Depth, cx); +} + // An UnwrapObject overload that just calls one of the JSObject* ones. template MOZ_ALWAYS_INLINE nsresult UnwrapObject(JS::Handle obj, U& value, @@ -378,6 +386,13 @@ MOZ_ALWAYS_INLINE nsresult UnwrapObject(JS::Handle obj, U& value, return UnwrapObject(&obj.toObject(), value, cx); } +template +MOZ_ALWAYS_INLINE nsresult UnwrapObject(JS::Handle obj, + NonNull& value, const CxType& cx) { + MOZ_ASSERT(obj.isObject()); + return UnwrapObject(&obj.toObject(), value, cx); +} + template MOZ_ALWAYS_INLINE void AssertStaticUnwrapOK() { static_assert(PrototypeID != prototypes::id::Window, @@ -3104,6 +3119,16 @@ bool GetSetlikeBackingObject(JSContext* aCx, JS::Handle aObj, JS::MutableHandle aBackingObj, bool* aBackingObjCreated); +// Unpacks backing object (ES Proxy exotic object) from the reserved slot of a +// reflector for a observableArray attribute. If backing object does not exist, +// creates backing object in the compartment of the reflector involved, making +// this safe to use across compartments/via xrays. Return values of these +// methods will always be in the context compartment. +bool GetObservableArrayBackingObject( + JSContext* aCx, JS::Handle aObj, size_t aSlotIndex, + JS::MutableHandle aBackingObj, bool* aBackingObjCreated, + const ObservableArrayProxyHandler* aHandler); + // Get the desired prototype object for an object construction from the given // CallArgs. The CallArgs must be for a constructor call. The // aProtoId/aCreator arguments are used to get a default if we don't find a diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index e3682e16af9b..9bcc1d4ca510 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -214,6 +214,7 @@ def idlTypeNeedsCallContext(type, descriptor=None, allowTreatNonCallableAsNull=F or type.isCallback() or type.isDictionary() or type.isRecord() + or type.isObservableArray() ): # These can all throw if a primitive is passed in, at the very least. # There are some rare cases when we know we have an object, but those @@ -1467,6 +1468,8 @@ class CGHeaders(CGWrapper): # Also add headers for the type the record is # parametrized over, if needed. addHeadersForType((t.inner, dictionary)) + elif unrolled.isObservableArray(): + bindingHeaders.add("mozilla/dom/ObservableArrayProxyHandler.h") for t in getAllTypes( descriptors + callbackDescriptors, dictionaries, callbacks @@ -2145,6 +2148,20 @@ def finalizeHook(descriptor, hookName, gcx, obj): """, obj=obj, ) + for m in descriptor.interface.members: + if m.isAttr() and m.type.isObservableArray(): + finalize += fill( + """ + { + JS::Value val = JS::GetReservedSlot(obj, ${slot}); + if (!val.isUndefined()) { + JSObject* obj = &val.toObject(); + js::SetProxyReservedSlot(obj, OBSERVABLE_ARRAY_DOM_INTERFACE_SLOT, JS::UndefinedValue()); + } + } + """, + slot=memberReservedSlot(m, descriptor), + ) if descriptor.wrapperCache: finalize += "ClearWrapper(self, self, %s);\n" % obj if descriptor.isGlobal(): @@ -5855,15 +5872,16 @@ def getJSToNativeConversionInfo( assert not (isEnforceRange and isClamp) # These are mutually exclusive - if type.isSequence(): + if type.isSequence() or type.isObservableArray(): assert not isEnforceRange and not isClamp and not isAllowShared if failureCode is None: notSequence = ( - 'cx.ThrowErrorMessage("%s", "sequence");\n' + 'cx.ThrowErrorMessage("%s", "%s");\n' "%s" % ( firstCap(sourceDescription), + "sequence" if type.isSequence() else "observable array", exceptionCode, ) ) @@ -8350,6 +8368,12 @@ def getWrapTemplateForType( # NB: setObject{,OrNull}(..., some-object-type) calls JS_WrapValue(), so is fallible return (head + setter(toValue % result, wrapAsType=type), False) + if type.isObservableArray(): + # This first argument isn't used at all for now, the attribute getter + # for ObservableArray type are generated in getObservableArrayGetterBody + # instead. + return "", False + if not ( type.isUnion() or type.isPrimitive() @@ -9509,6 +9533,9 @@ class CGPerSignatureCall(CGThing): idlNode.identifier.name, ) ) + elif idlNode.isAttr() and idlNode.type.isObservableArray(): + assert setter + cgThings.append(CGObservableArraySetterGenerator(descriptor, idlNode)) else: context = GetLabelForErrorReporting(descriptor, idlNode, isConstructor) if getter: @@ -10482,17 +10509,15 @@ class CGSetterCall(CGPerSignatureCall): def wrap_return_value(self): attr = self.idlNode + clearSlot = "" if self.descriptor.wrapperCache and attr.slotIndices is not None: if attr.getExtendedAttribute("StoreInSlot"): - args = "cx, self" - else: + clearSlot = "%s(cx, self);\n" % MakeClearCachedValueNativeName( + self.idlNode + ) + elif attr.getExtendedAttribute("Cached"): args = "self" - clearSlot = "%s(%s);\n" % ( - MakeClearCachedValueNativeName(self.idlNode), - args, - ) - else: - clearSlot = "" + clearSlot = "%s(self);\n" % MakeClearCachedValueNativeName(self.idlNode) # We have no return value return "\n" "%s" "return true;\n" % clearSlot @@ -11210,6 +11235,13 @@ class CGSpecializedGetter(CGAbstractStaticMethod): return prefix + getMaplikeOrSetlikeSizeGetterBody( self.descriptor, self.attr ) + + if self.attr.type.isObservableArray(): + assert not self.attr.getExtendedAttribute("CrossOriginReadable") + # If the attribute is observableArray, due to having to unpack the + # backing object from the slot, this requires its own generator. + return prefix + getObservableArrayGetterBody(self.descriptor, self.attr) + nativeName = CGSpecializedGetter.makeNativeName(self.descriptor, self.attr) type = self.attr.type if self.attr.getExtendedAttribute("CrossOriginReadable"): @@ -11457,6 +11489,7 @@ class CGSpecializedSetter(CGAbstractStaticMethod): nativeType=self.descriptor.nativeType, call=call, ) + return prefix + fill( """ auto* self = static_cast<${nativeType}*>(void_self); @@ -11809,8 +11842,13 @@ class CGMemberJITInfo(CGThing): and infallibleForMember(self.member, self.member.type, self.descriptor) ) isAlwaysInSlot = self.member.getExtendedAttribute("StoreInSlot") + if self.member.slotIndices is not None: - assert isAlwaysInSlot or self.member.getExtendedAttribute("Cached") + assert ( + isAlwaysInSlot + or self.member.getExtendedAttribute("Cached") + or self.member.type.isObservableArray() + ) isLazilyCachedInSlot = not isAlwaysInSlot slotIndex = memberReservedSlot(self.member, self.descriptor) # We'll statically assert that this is not too big in @@ -12034,6 +12072,8 @@ class CGMemberJITInfo(CGThing): ) if t.isDictionary(): return "JSVAL_TYPE_OBJECT" + if t.isObservableArray(): + return "JSVAL_TYPE_OBJECT" if not t.isPrimitive(): raise TypeError("No idea what type " + str(t) + " is.") tag = t.tag() @@ -15745,18 +15785,20 @@ class CGDOMJSProxyHandler_getElements(ClassMethod): ) -class CGDOMJSProxyHandler_getInstance(ClassMethod): - def __init__(self): +class CGJSProxyHandler_getInstance(ClassMethod): + def __init__(self, type): + self.type = type ClassMethod.__init__( - self, "getInstance", "const DOMProxyHandler*", [], static=True + self, "getInstance", "const %s*" % self.type, [], static=True ) def getBody(self): - return dedent( + return fill( """ - static const DOMProxyHandler instance; + static const ${type} instance; return &instance; - """ + """, + type=self.type, ) @@ -16048,7 +16090,7 @@ class CGDOMJSProxyHandler(CGClass): CGDOMJSProxyHandler_className(descriptor), CGDOMJSProxyHandler_finalizeInBackground(descriptor), CGDOMJSProxyHandler_finalize(descriptor), - CGDOMJSProxyHandler_getInstance(), + CGJSProxyHandler_getInstance("DOMProxyHandler"), CGDOMJSProxyHandler_delete(descriptor), ] constructors = [ @@ -16245,6 +16287,11 @@ class CGDescriptor(CGThing): elif m.isMaplikeOrSetlike(): cgThings.append(CGMaplikeOrSetlikeHelperGenerator(descriptor, m)) elif m.isAttr(): + if m.type.isObservableArray(): + cgThings.append( + CGObservableArrayProxyHandlerGenerator(descriptor, m) + ) + cgThings.append(CGObservableArrayHelperGenerator(descriptor, m)) if m.getExtendedAttribute("Unscopable"): assert not m.isStatic() unscopableNames.append(m.identifier.name) @@ -18126,11 +18173,19 @@ class CGBindingRoot(CGThing): return any(hasCrossOriginProperty(m) for m in desc.interface.members) + def descriptorHasObservableArrayTypes(desc): + def hasObservableArrayTypes(m): + return m.isAttr() and m.type.isObservableArray() + + return any(hasObservableArrayTypes(m) for m in desc.interface.members) + bindingDeclareHeaders["mozilla/dom/RemoteObjectProxy.h"] = any( descriptorHasCrossOriginProperties(d) for d in descriptors ) bindingDeclareHeaders["jsapi.h"] = any( - descriptorHasCrossOriginProperties(d) for d in descriptors + descriptorHasCrossOriginProperties(d) + or descriptorHasObservableArrayTypes(d) + for d in descriptors ) bindingDeclareHeaders["js/TypeDecls.h"] = not bindingDeclareHeaders["jsapi.h"] bindingDeclareHeaders["js/RootingAPI.h"] = not bindingDeclareHeaders["jsapi.h"] @@ -18194,6 +18249,10 @@ class CGBindingRoot(CGThing): d.concrete and d.proxy for d in descriptors ) + bindingHeaders["mozilla/dom/ProxyHandlerUtils.h"] = any( + d.concrete and d.proxy for d in descriptors + ) + bindingHeaders["js/String.h"] = any( d.needsMissingPropUseCounters for d in descriptors ) @@ -19145,7 +19204,7 @@ class CGBindingImplClass(CGClass): if not m.isStatic() or not skipStaticMethods: appendMethod(m) elif m.isAttr(): - if m.isMaplikeOrSetlikeAttr(): + if m.isMaplikeOrSetlikeAttr() or m.type.isObservableArray(): # Handled by generated code already continue self.methodDecls.append(cgGetter(descriptor, m)) @@ -19308,6 +19367,42 @@ class CGBindingImplClass(CGClass): return self._deps +class CGExampleObservableArrayCallback(CGNativeMember): + def __init__(self, descriptor, attr, callbackName): + assert attr.isAttr() + assert attr.type.isObservableArray() + CGNativeMember.__init__( + self, + descriptor, + attr, + self.makeNativeName(attr, callbackName), + ( + BuiltinTypes[IDLBuiltinType.Types.void], + [ + FakeArgument(attr.type.inner, "aValue"), + FakeArgument( + BuiltinTypes[IDLBuiltinType.Types.unsigned_long], "aIndex" + ), + ], + ), + ["needsErrorResult"], + ) + + def declare(self, cgClass): + assert self.member.isAttr() + assert self.member.type.isObservableArray() + return CGNativeMember.declare(self, cgClass) + + def define(self, cgClass): + return "" + + @staticmethod + def makeNativeName(attr, callbackName): + assert attr.isAttr() + nativeName = MakeNativeName(attr.identifier.name) + return "On" + callbackName + nativeName + + class CGExampleClass(CGBindingImplClass): """ Codegen for the actual example class implementation for this descriptor @@ -19365,6 +19460,15 @@ class CGExampleClass(CGBindingImplClass): else: decorators = "final" + for m in descriptor.interface.members: + if m.isAttr() and m.type.isObservableArray(): + self.methodDecls.append( + CGExampleObservableArrayCallback(descriptor, m, "Set") + ) + self.methodDecls.append( + CGExampleObservableArrayCallback(descriptor, m, "Delete") + ) + CGClass.__init__( self, self.nativeLeafName(descriptor), @@ -21451,7 +21555,7 @@ class CGMaplikeOrSetlikeMethodGenerator(CGThing): def appendBoolResult(self): if self.helperImpl: - return ([CGGeneric()], ["&aRetVal"], []) + return ([CGGeneric("bool retVal;\n")], ["&retVal"], []) return ([CGGeneric("bool result;\n")], ["&result"], []) def forEach(self): @@ -21528,7 +21632,7 @@ class CGMaplikeOrSetlikeMethodGenerator(CGThing): code = [] # We don't need to create the result variable because it'll be created elsewhere # for JSObject Get method - if not self.helperImpl or not self.helperImpl.handleJSObjectGetHelper(): + if not self.helperImpl or not self.helperImpl.needsScopeBody(): code = [ CGGeneric( dedent( @@ -21597,19 +21701,20 @@ class CGMaplikeOrSetlikeMethodGenerator(CGThing): return self.cgRoot.define() -class CGMaplikeOrSetlikeHelperFunctionGenerator(CallbackMember): +class CGHelperFunctionGenerator(CallbackMember): """ - Generates code to allow C++ to perform operations on backing objects. Gets - a context from the binding wrapper, turns arguments into JS::Values (via + Generates code to allow C++ to perform operations. Gets a context from the + binding wrapper, turns arguments into JS::Values (via CallbackMember/CGNativeMember argument conversion), then uses - CGMaplikeOrSetlikeMethodGenerator to generate the body. - + getCall to generate the body for getting result, and maybe convert the + result into return type (via CallbackMember/CGNativeMember result + conversion) """ class HelperFunction(CGAbstractMethod): """ Generates context retrieval code and rooted JSObject for interface for - CGMaplikeOrSetlikeMethodGenerator to use + method generator to use """ def __init__(self, descriptor, name, args, code, returnType): @@ -21622,31 +21727,14 @@ class CGMaplikeOrSetlikeHelperFunctionGenerator(CallbackMember): def __init__( self, descriptor, - maplikeOrSetlike, name, - needsKeyArg=False, - needsValueArg=False, - needsValueTypeReturn=False, - needsBoolReturn=False, + args, + returnType=BuiltinTypes[IDLBuiltinType.Types.void], + needsResultConversion=True, ): - assert not (needsValueTypeReturn and needsBoolReturn) - args = [] - self.maplikeOrSetlike = maplikeOrSetlike - self.needsBoolReturn = needsBoolReturn - self.needsValueTypeReturn = needsValueTypeReturn + assert returnType.isType() + self.needsResultConversion = needsResultConversion - returnType = ( - BuiltinTypes[IDLBuiltinType.Types.void] - if not self.needsValueTypeReturn - else maplikeOrSetlike.valueType - ) - - if needsKeyArg: - args.append(FakeArgument(maplikeOrSetlike.keyType, "aKey")) - if needsValueArg: - assert needsKeyArg - assert not needsValueTypeReturn - args.append(FakeArgument(maplikeOrSetlike.valueType, "aValue")) # Run CallbackMember init function to generate argument conversion code. # wrapScope is set to 'obj' when generating maplike or setlike helper # functions, as we don't have access to the CallbackPreserveColor @@ -21658,26 +21746,21 @@ class CGMaplikeOrSetlikeHelperFunctionGenerator(CallbackMember): descriptor, False, wrapScope="obj", - passJSBitsAsNeeded=self.handleJSObjectGetHelper(), + passJSBitsAsNeeded=typeNeedsCx(returnType), ) - if self.needsValueTypeReturn: - finalReturnType = self.returnType - elif needsBoolReturn: - finalReturnType = "bool" - else: - finalReturnType = "void" # Wrap CallbackMember body code into a CGAbstractMethod to make # generation easier. - self.implMethod = CGMaplikeOrSetlikeHelperFunctionGenerator.HelperFunction( - descriptor, name, self.args, self.body, finalReturnType + self.implMethod = CGHelperFunctionGenerator.HelperFunction( + descriptor, name, self.args, self.body, self.returnType ) def getCallSetup(self): - # If handleJSObjectGetHelper is true, it means the caller will provide a JSContext, - # so we don't need to create JSContext and enter UnprivilegedJunkScopeOrWorkerGlobal here. + # If passJSBitsAsNeeded is true, it means the caller will provide a + # JSContext, so we don't need to create JSContext and enter + # UnprivilegedJunkScopeOrWorkerGlobal here. code = "MOZ_ASSERT(self);\n" - if not self.handleJSObjectGetHelper(): + if not self.passJSBitsAsNeeded: code += dedent( """ AutoJSAPI jsapi; @@ -21711,21 +21794,12 @@ class CGMaplikeOrSetlikeHelperFunctionGenerator(CallbackMember): % self.getDefaultRetval() ) - # For the JSObject Get method, we'd like wrap the inner code in a scope such that - # the code can use the same realm. So here we are creating the result variable - # outside of the scope. - if self.handleJSObjectGetHelper(): - code += dedent( - """ - JS::Rooted result(cx); - """ - ) - else: - code += dedent( - """ - JSAutoRealm reflectorRealm(cx, obj); - """ - ) + # We'd like wrap the inner code in a scope such that the code can use the + # same realm. So here we are creating the result variable outside of the + # scope. + if self.needsScopeBody(): + code += "JS::Rooted result(cx);\n" + return code def getArgs(self, returnType, argList): @@ -21735,20 +21809,15 @@ class CGMaplikeOrSetlikeHelperFunctionGenerator(CallbackMember): return [Argument(self.descriptorProvider.nativeType + "*", "self")] + args def needsScopeBody(self): - return self.handleJSObjectGetHelper() + return self.passJSBitsAsNeeded def getArgvDeclFailureCode(self): return "aRv.Throw(NS_ERROR_UNEXPECTED);\n" - def handleJSObjectGetHelper(self): - return self.needsValueTypeReturn and self.maplikeOrSetlike.valueType.isObject() - def getResultConversion(self): - if self.needsBoolReturn: - return "return aRetVal;\n" - elif self.needsValueTypeReturn: + if self.needsResultConversion: code = "" - if self.handleJSObjectGetHelper(): + if self.needsScopeBody(): code = dedent( """ if (!JS_WrapValue(cx, &result)) { @@ -21761,7 +21830,7 @@ class CGMaplikeOrSetlikeHelperFunctionGenerator(CallbackMember): failureCode = dedent("aRv.Throw(NS_ERROR_UNEXPECTED);\nreturn nullptr;\n") exceptionCode = None - if self.maplikeOrSetlike.valueType.isPrimitive(): + if self.retvalType.isPrimitive(): exceptionCode = dedent( "aRv.NoteJSContextException(cx);\nreturn%s;\n" % self.getDefaultRetval() @@ -21774,34 +21843,26 @@ class CGMaplikeOrSetlikeHelperFunctionGenerator(CallbackMember): isDefinitelyObject=True, exceptionCode=exceptionCode, ) - return "return;\n" + + assignRetval = string.Template( + self.getRetvalInfo(self.retvalType, False)[2] + ).substitute( + { + "declName": "retVal", + } + ) + return assignRetval def getRvalDecl(self): - if self.needsBoolReturn: - return "bool aRetVal;\n" - elif self.handleJSObjectGetHelper(): - return "JSAutoRealm reflectorRealm(cx, obj);\n" - return "" + # hack to make sure we put JSAutoRealm inside the body scope + return "JSAutoRealm reflectorRealm(cx, obj);\n" def getArgcDecl(self): # Don't need argc for anything. return None - def getDefaultRetval(self): - if self.needsBoolReturn: - return " false" - elif self.needsValueTypeReturn: - return CallbackMember.getDefaultRetval(self) - return "" - def getCall(self): - return CGMaplikeOrSetlikeMethodGenerator( - self.descriptorProvider, - self.maplikeOrSetlike, - self.name.lower(), - self.needsValueTypeReturn, - helperImpl=self, - ).define() + assert False # Override me! def getPrettyName(self): return self.name @@ -21813,6 +21874,61 @@ class CGMaplikeOrSetlikeHelperFunctionGenerator(CallbackMember): return self.implMethod.define() +class CGMaplikeOrSetlikeHelperFunctionGenerator(CGHelperFunctionGenerator): + """ + Generates code to allow C++ to perform operations on backing objects. Gets + a context from the binding wrapper, turns arguments into JS::Values (via + CallbackMember/CGNativeMember argument conversion), then uses + CGMaplikeOrSetlikeMethodGenerator to generate the body. + """ + + def __init__( + self, + descriptor, + maplikeOrSetlike, + name, + needsKeyArg=False, + needsValueArg=False, + needsValueTypeReturn=False, + needsBoolReturn=False, + needsResultConversion=True, + ): + self.maplikeOrSetlike = maplikeOrSetlike + self.needsValueTypeReturn = needsValueTypeReturn + + args = [] + if needsKeyArg: + args.append(FakeArgument(maplikeOrSetlike.keyType, "aKey")) + if needsValueArg: + assert needsKeyArg + assert not needsValueTypeReturn + args.append(FakeArgument(maplikeOrSetlike.valueType, "aValue")) + + returnType = BuiltinTypes[IDLBuiltinType.Types.void] + if needsBoolReturn: + returnType = BuiltinTypes[IDLBuiltinType.Types.boolean] + elif needsValueTypeReturn: + returnType = maplikeOrSetlike.valueType + + CGHelperFunctionGenerator.__init__( + self, + descriptor, + name, + args, + returnType, + needsResultConversion, + ) + + def getCall(self): + return CGMaplikeOrSetlikeMethodGenerator( + self.descriptorProvider, + self.maplikeOrSetlike, + self.name.lower(), + self.needsValueTypeReturn, + helperImpl=self, + ).define() + + class CGMaplikeOrSetlikeHelperGenerator(CGNamespace): """ Declares and defines convenience methods for accessing backing objects on @@ -21839,6 +21955,7 @@ class CGMaplikeOrSetlikeHelperGenerator(CGNamespace): "Delete", needsKeyArg=True, needsBoolReturn=True, + needsResultConversion=False, ), CGMaplikeOrSetlikeHelperFunctionGenerator( descriptor, @@ -21846,6 +21963,7 @@ class CGMaplikeOrSetlikeHelperGenerator(CGNamespace): "Has", needsKeyArg=True, needsBoolReturn=True, + needsResultConversion=False, ), ] if self.maplikeOrSetlike.isMaplike(): @@ -21936,6 +22054,648 @@ class CGIterableMethodGenerator(CGGeneric): ) +def getObservableArrayBackingObject(descriptor, attr, errorReturn="return false;\n"): + """ + Generate code to get/create a JS backing list for an observableArray attribute + from the declaration slot. + """ + assert attr.isAttr() + assert attr.type.isObservableArray() + + return fill( + """ + JS::Rooted backingObj(cx); + bool created = false; + if (!GetObservableArrayBackingObject(cx, obj, ${slot}, + &backingObj, &created, ${namespace}::ObservableArrayProxyHandler::getInstance())) { + $*{errorReturn} + } + if (created) { + PreserveWrapper(self); + js::SetProxyReservedSlot(backingObj, + OBSERVABLE_ARRAY_DOM_INTERFACE_SLOT, + JS::PrivateValue(self)); + } + """, + namespace=toBindingNamespace(MakeNativeName(attr.identifier.name)), + slot=memberReservedSlot(attr, descriptor), + errorReturn=errorReturn, + selfType=descriptor.nativeType, + ) + + +def getObservableArrayGetterBody(descriptor, attr): + """ + Creates the body for the getter method of an observableArray attribute. + """ + assert attr.type.isObservableArray() + return fill( + """ + if (xpc::WrapperFactory::IsXrayWrapper(obj)) { + JS_ReportErrorASCII(cx, "Accessing from Xray wrapper is not supported."); + return false; + } + $*{getBackingObj} + MOZ_ASSERT(!JS_IsExceptionPending(cx)); + args.rval().setObject(*backingObj); + return true; + """, + getBackingObj=getObservableArrayBackingObject(descriptor, attr), + ) + + +class CGObservableArrayProxyHandler_callback(ClassMethod): + """ + Base class for declaring and defining the hook methods for ObservableArrayProxyHandler + subclasses to get the interface native object from backing object and calls + its On{Set|Delete}* callback. + + * 'callbackType': "Set" or "Delete". + * 'invalidTypeFatal' (optional): If True, we don't expect the type + conversion would fail, so generate the + assertion code if type conversion fails. + """ + + def __init__( + self, descriptor, attr, name, args, callbackType, invalidTypeFatal=False + ): + assert attr.isAttr() + assert attr.type.isObservableArray() + assert callbackType in ["Set", "Delete"] + self.descriptor = descriptor + self.attr = attr + self.innertype = attr.type.inner + self.callbackType = callbackType + self.invalidTypeFatal = invalidTypeFatal + ClassMethod.__init__( + self, + name, + "bool", + args, + visibility="protected", + virtual=True, + override=True, + const=True, + ) + + def preConversion(self): + """ + The code to run before the conversion steps. + """ + return "" + + def preCallback(self): + """ + The code to run before calling the callback. + """ + return "" + + def postCallback(self): + """ + The code to run after calling the callback, all subclasses should override + this to generate the return values. + """ + assert False # Override me! + + def getBody(self): + exceptionCode = ( + fill( + """ + MOZ_ASSERT_UNREACHABLE("The item in ObservableArray backing list is not ${innertype}?"); + return false; + """, + innertype=self.innertype, + ) + if self.invalidTypeFatal + else None + ) + convertType = instantiateJSToNativeConversion( + getJSToNativeConversionInfo( + self.innertype, + self.descriptor, + sourceDescription="Element in ObservableArray backing list", + exceptionCode=exceptionCode, + ), + { + "declName": "decl", + "holderName": "holder", + "val": "aValue", + }, + ) + callbackArgs = ["decl", "aIndex", "rv"] + if typeNeedsCx(self.innertype): + callbackArgs.insert(0, "cx") + return fill( + """ + MOZ_ASSERT(IsObservableArrayProxy(aProxy)); + $*{preConversion} + + BindingCallContext cx(aCx, "ObservableArray ${name}"); + $*{convertType} + + $*{preCallback} + JS::Value val = js::GetProxyReservedSlot(aProxy, OBSERVABLE_ARRAY_DOM_INTERFACE_SLOT); + auto* interface = static_cast<${ifaceType}*>(val.toPrivate()); + MOZ_ASSERT(interface); + + ErrorResult rv; + MOZ_KnownLive(interface)->${methodName}(${callbackArgs}); + $*{postCallback} + """, + preConversion=self.preConversion(), + name=self.name, + convertType=convertType.define(), + preCallback=self.preCallback(), + ifaceType=self.descriptor.nativeType, + methodName="On%s%s" + % (self.callbackType, MakeNativeName(self.attr.identifier.name)), + callbackArgs=", ".join(callbackArgs), + postCallback=self.postCallback(), + ) + + +class CGObservableArrayProxyHandler_OnDeleteItem( + CGObservableArrayProxyHandler_callback +): + """ + Declares and defines the hook methods for ObservableArrayProxyHandler + subclasses to get the interface native object from backing object and calls + its OnDelete* callback. + """ + + def __init__(self, descriptor, attr): + args = [ + Argument("JSContext*", "aCx"), + Argument("JS::HandleObject", "aProxy"), + Argument("JS::HandleValue", "aValue"), + Argument("uint32_t", "aIndex"), + ] + CGObservableArrayProxyHandler_callback.__init__( + self, + descriptor, + attr, + "OnDeleteItem", + args, + "Delete", + True, + ) + + def postCallback(self): + return dedent( + """ + return !rv.MaybeSetPendingException(cx); + """ + ) + + +class CGObservableArrayProxyHandler_SetIndexedValue( + CGObservableArrayProxyHandler_callback +): + """ + Declares and defines the hook methods for ObservableArrayProxyHandler + subclasses to run the setting the indexed value steps. + """ + + def __init__(self, descriptor, attr): + args = [ + Argument("JSContext*", "aCx"), + Argument("JS::HandleObject", "aProxy"), + Argument("JS::HandleObject", "aBackingList"), + Argument("uint32_t", "aIndex"), + Argument("JS::HandleValue", "aValue"), + Argument("JS::ObjectOpResult&", "aResult"), + ] + CGObservableArrayProxyHandler_callback.__init__( + self, + descriptor, + attr, + "SetIndexedValue", + args, + "Set", + ) + + def preConversion(self): + return dedent( + """ + uint32_t oldLen; + if (!JS::GetArrayLength(aCx, aBackingList, &oldLen)) { + return false; + } + + if (aIndex > oldLen) { + return aResult.failBadIndex(); + } + """ + ) + + def preCallback(self): + return dedent( + """ + if (aIndex < oldLen) { + JS::RootedValue value(aCx); + if (!JS_GetElement(aCx, aBackingList, aIndex, &value)) { + return false; + } + + if (!OnDeleteItem(aCx, aProxy, value, aIndex)) { + return false; + } + } + + """ + ) + + def postCallback(self): + return dedent( + """ + if (rv.MaybeSetPendingException(cx)) { + return false; + } + + if (!JS_SetElement(aCx, aBackingList, aIndex, aValue)) { + return false; + } + + return aResult.succeed(); + """ + ) + + +class CGObservableArrayProxyHandler(CGThing): + """ + A class for declaring a ObservableArrayProxyHandler. + """ + + def __init__(self, descriptor, attr): + assert attr.isAttr() + assert attr.type.isObservableArray() + methods = [ + # The item stored in backing object should always be converted successfully. + CGObservableArrayProxyHandler_OnDeleteItem(descriptor, attr), + CGObservableArrayProxyHandler_SetIndexedValue(descriptor, attr), + CGJSProxyHandler_getInstance("ObservableArrayProxyHandler"), + ] + parentClass = "mozilla::dom::ObservableArrayProxyHandler" + self.proxyHandler = CGClass( + "ObservableArrayProxyHandler", + bases=[ClassBase(parentClass)], + constructors=[], + methods=methods, + ) + + def declare(self): + # Our class declaration should happen when we're defining + return "" + + def define(self): + return self.proxyHandler.declare() + "\n" + self.proxyHandler.define() + + +class CGObservableArrayProxyHandlerGenerator(CGNamespace): + """ + Declares and defines convenience methods for accessing backing list objects + for observable array attribute. Generates function signatures, un/packs + backing list objects from slot, etc. + """ + + def __init__(self, descriptor, attr): + assert attr.isAttr() + assert attr.type.isObservableArray() + namespace = toBindingNamespace(MakeNativeName(attr.identifier.name)) + proxyHandler = CGObservableArrayProxyHandler(descriptor, attr) + CGNamespace.__init__(self, namespace, proxyHandler) + + +class CGObservableArraySetterGenerator(CGGeneric): + """ + Creates setter for an observableArray attributes. + """ + + def __init__(self, descriptor, attr): + assert attr.isAttr() + assert attr.type.isObservableArray() + getBackingObject = getObservableArrayBackingObject(descriptor, attr) + setElement = dedent( + """ + if (!JS_SetElement(cx, backingObj, i, val)) { + return false; + } + """ + ) + conversion = wrapForType( + attr.type.inner, + descriptor, + { + "result": "arg0.ElementAt(i)", + "successCode": setElement, + "jsvalRef": "val", + "jsvalHandle": "&val", + }, + ) + CGGeneric.__init__( + self, + fill( + """ + if (xpc::WrapperFactory::IsXrayWrapper(obj)) { + JS_ReportErrorASCII(cx, "Accessing from Xray wrapper is not supported."); + return false; + } + + ${getBackingObject} + const ObservableArrayProxyHandler* handler = GetObservableArrayProxyHandler(backingObj); + if (!handler->SetLength(cx, backingObj, 0)) { + return false; + } + + JS::Rooted val(cx); + for (size_t i = 0; i < arg0.Length(); i++) { + $*{conversion} + } + """, + conversion=conversion, + getBackingObject=getBackingObject, + ), + ) + + +class CGObservableArrayHelperFunctionGenerator(CGHelperFunctionGenerator): + """ + Generates code to allow C++ to perform operations on backing objects. Gets + a context from the binding wrapper, turns arguments into JS::Values (via + CallbackMember/CGNativeMember argument conversion), then uses + MethodBodyGenerator to generate the body. + """ + + class MethodBodyGenerator(CGThing): + """ + Creates methods body for observable array attribute. It is expected that all + methods will be have a maplike/setlike object attached. Unwrapping/wrapping + will be taken care of by the usual method generation machinery in + CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of + using CGCallGenerator. + """ + + def __init__( + self, + descriptor, + attr, + methodName, + helperGenerator, + needsIndexArg, + ): + assert attr.isAttr() + assert attr.type.isObservableArray() + + CGThing.__init__(self) + self.helperGenerator = helperGenerator + self.cgRoot = CGList([]) + + self.cgRoot.append( + CGGeneric( + getObservableArrayBackingObject( + descriptor, + attr, + dedent( + """ + aRv.Throw(NS_ERROR_UNEXPECTED); + return%s; + """ + % helperGenerator.getDefaultRetval() + ), + ) + ) + ) + + # Generates required code for the method. Method descriptions included + # in definitions below. Throw if we don't have a method to fill in what + # we're looking for. + try: + methodGenerator = getattr(self, methodName) + except AttributeError: + raise TypeError( + "Missing observable array method definition '%s'" % methodName + ) + # Method generator returns tuple, containing: + # + # - a list of CGThings representing setup code for preparing to call + # the JS API function + # - JS API function name + # - a list of arguments needed for the JS API function we're calling + # - a list of CGThings representing code needed before return. + (setupCode, funcName, arguments, returnCode) = methodGenerator() + + # Append the list of setup code CGThings + self.cgRoot.append(CGList(setupCode)) + # Create the JS API call + if needsIndexArg: + arguments.insert(0, "aIndex") + self.cgRoot.append( + CGWrapper( + CGGeneric( + fill( + """ + aRv.MightThrowJSException(); + if (!${funcName}(${args})) { + aRv.StealExceptionFromJSContext(cx); + return${retval}; + } + """, + funcName=funcName, + args=", ".join(["cx", "backingObj"] + arguments), + retval=helperGenerator.getDefaultRetval(), + ) + ) + ) + ) + # Append code before return + self.cgRoot.append(CGList(returnCode)) + + def elementat(self): + setupCode = [] + if not self.helperGenerator.needsScopeBody(): + setupCode.append(CGGeneric("JS::Rooted result(cx);\n")) + returnCode = [ + CGGeneric( + fill( + """ + if (result.isUndefined()) { + aRv.Throw(NS_ERROR_NOT_AVAILABLE); + return${retval}; + } + """, + retval=self.helperGenerator.getDefaultRetval(), + ) + ) + ] + return (setupCode, "JS_GetElement", ["&result"], returnCode) + + def replaceelementat(self): + setupCode = [ + CGGeneric( + fill( + """ + uint32_t length; + aRv.MightThrowJSException(); + if (!JS::GetArrayLength(cx, backingObj, &length)) { + aRv.StealExceptionFromJSContext(cx); + return${retval}; + } + if (aIndex > length) { + aRv.ThrowRangeError("Invalid index"); + return${retval}; + } + """, + retval=self.helperGenerator.getDefaultRetval(), + ) + ) + ] + return (setupCode, "JS_SetElement", ["argv[0]"], []) + + def appendelement(self): + setupCode = [ + CGGeneric( + fill( + """ + uint32_t length; + aRv.MightThrowJSException(); + if (!JS::GetArrayLength(cx, backingObj, &length)) { + aRv.StealExceptionFromJSContext(cx); + return${retval}; + } + """, + retval=self.helperGenerator.getDefaultRetval(), + ) + ) + ] + return (setupCode, "JS_SetElement", ["length", "argv[0]"], []) + + def removelastelement(self): + setupCode = [ + CGGeneric( + fill( + """ + uint32_t length; + aRv.MightThrowJSException(); + if (!JS::GetArrayLength(cx, backingObj, &length)) { + aRv.StealExceptionFromJSContext(cx); + return${retval}; + } + if (length == 0) { + aRv.Throw(NS_ERROR_NOT_AVAILABLE); + return${retval}; + } + """, + retval=self.helperGenerator.getDefaultRetval(), + ) + ) + ] + return (setupCode, "JS::SetArrayLength", ["length - 1"], []) + + def length(self): + return ( + [CGGeneric("uint32_t retVal;\n")], + "JS::GetArrayLength", + ["&retVal"], + [], + ) + + def define(self): + return self.cgRoot.define() + + def __init__( + self, + descriptor, + attr, + name, + returnType=BuiltinTypes[IDLBuiltinType.Types.void], + needsResultConversion=True, + needsIndexArg=False, + needsValueArg=False, + ): + assert attr.isAttr() + assert attr.type.isObservableArray() + self.attr = attr + self.needsIndexArg = needsIndexArg + + args = [] + if needsValueArg: + args.append(FakeArgument(attr.type.inner, "aValue")) + + CGHelperFunctionGenerator.__init__( + self, + descriptor, + name, + args, + returnType, + needsResultConversion, + ) + + def getArgs(self, returnType, argList): + if self.needsIndexArg: + argList = [ + FakeArgument(BuiltinTypes[IDLBuiltinType.Types.unsigned_long], "aIndex") + ] + argList + return CGHelperFunctionGenerator.getArgs(self, returnType, argList) + + def getCall(self): + return CGObservableArrayHelperFunctionGenerator.MethodBodyGenerator( + self.descriptorProvider, + self.attr, + self.name.lower(), + self, + self.needsIndexArg, + ).define() + + +class CGObservableArrayHelperGenerator(CGNamespace): + """ + Declares and defines convenience methods for accessing backing object for + observable array type. Generates function signatures, un/packs + backing objects from slot, etc. + """ + + def __init__(self, descriptor, attr): + assert attr.isAttr() + assert attr.type.isObservableArray() + + namespace = "%sHelpers" % MakeNativeName(attr.identifier.name) + helpers = [ + CGObservableArrayHelperFunctionGenerator( + descriptor, + attr, + "ElementAt", + returnType=attr.type.inner, + needsIndexArg=True, + ), + CGObservableArrayHelperFunctionGenerator( + descriptor, + attr, + "ReplaceElementAt", + needsIndexArg=True, + needsValueArg=True, + ), + CGObservableArrayHelperFunctionGenerator( + descriptor, + attr, + "AppendElement", + needsValueArg=True, + ), + CGObservableArrayHelperFunctionGenerator( + descriptor, + attr, + "RemoveLastElement", + ), + CGObservableArrayHelperFunctionGenerator( + descriptor, + attr, + "Length", + returnType=BuiltinTypes[IDLBuiltinType.Types.unsigned_long], + needsResultConversion=False, + ), + ] + CGNamespace.__init__(self, namespace, CGList(helpers, "\n")) + + class GlobalGenRoots: """ Roots for global codegen. diff --git a/dom/bindings/DOMJSProxyHandler.h b/dom/bindings/DOMJSProxyHandler.h index a2f9ebf72d0e..619381bf3323 100644 --- a/dom/bindings/DOMJSProxyHandler.h +++ b/dom/bindings/DOMJSProxyHandler.h @@ -7,20 +7,12 @@ #ifndef mozilla_dom_DOMJSProxyHandler_h #define mozilla_dom_DOMJSProxyHandler_h -#include "mozilla/Attributes.h" -#include "mozilla/Likely.h" +#include "mozilla/Assertions.h" #include "mozilla/Maybe.h" -#include "mozilla/TextUtils.h" #include "jsapi.h" #include "js/Object.h" // JS::GetClass #include "js/Proxy.h" -#include "js/String.h" // JS::AtomToLinearString, JS::GetLinearString{CharAt,Length} -#include "nsString.h" - -// XXX Avoid including this (and maybe some of those above by moving inline -// function bodies out) -#include "jsfriendapi.h" namespace mozilla { namespace dom { @@ -173,42 +165,6 @@ inline const DOMProxyHandler* GetDOMProxyHandler(JSObject* obj) { return static_cast(js::GetProxyHandler(obj)); } -extern jsid s_length_id; - -// A return value of UINT32_MAX indicates "not an array index". Note, in -// particular, that UINT32_MAX itself is not a valid array index in general. -inline uint32_t GetArrayIndexFromId(JS::Handle id) { - // Much like js::IdIsIndex, except with a fast path for "length" and another - // fast path for starting with a lowercase ascii char. Is that second one - // really needed? I guess it is because StringIsArrayIndex is out of line... - // as of now, use id.get() instead of id otherwise operands mismatch error - // occurs. - if (MOZ_LIKELY(id.isInt())) { - return id.toInt(); - } - if (MOZ_LIKELY(id.get() == s_length_id)) { - return UINT32_MAX; - } - if (MOZ_UNLIKELY(!id.isAtom())) { - return UINT32_MAX; - } - - JSLinearString* str = JS::AtomToLinearString(id.toAtom()); - if (MOZ_UNLIKELY(JS::GetLinearStringLength(str) == 0)) { - return UINT32_MAX; - } - - char16_t firstChar = JS::GetLinearStringCharAt(str, 0); - if (MOZ_LIKELY(IsAsciiLowercaseAlpha(firstChar))) { - return UINT32_MAX; - } - - uint32_t i; - return js::StringIsArrayIndex(str, &i) ? i : UINT32_MAX; -} - -inline bool IsArrayIndex(uint32_t index) { return index < UINT32_MAX; } - } // namespace dom } // namespace mozilla diff --git a/dom/bindings/JSSlots.h b/dom/bindings/JSSlots.h index 5e19957d63ca..5b9d91ccf315 100644 --- a/dom/bindings/JSSlots.h +++ b/dom/bindings/JSSlots.h @@ -31,4 +31,11 @@ // slot for the unforgeable holder is needed. #define DOM_INTERFACE_PROTO_SLOTS_BASE 0 +// The slot index of raw pointer of dom object stored in observable array exotic +// object. We nedd this in order to call the OnSet* and OnDelete* callbacks. +#define OBSERVABLE_ARRAY_DOM_INTERFACE_SLOT 0 + +// The slot index of backing list stored in observable array exotic object. +#define OBSERVABLE_ARRAY_BACKING_LIST_OBJECT_SLOT 1 + #endif /* mozilla_dom_DOMSlots_h */ diff --git a/dom/bindings/ObservableArrayProxyHandler.cpp b/dom/bindings/ObservableArrayProxyHandler.cpp new file mode 100644 index 000000000000..7c8f727e195b --- /dev/null +++ b/dom/bindings/ObservableArrayProxyHandler.cpp @@ -0,0 +1,366 @@ +/* -*- 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 "mozilla/dom/ObservableArrayProxyHandler.h" + +#include "jsapi.h" +#include "js/friend/ErrorMessages.h" +#include "js/Conversions.h" +#include "js/Object.h" +#include "mozilla/dom/JSSlots.h" +#include "mozilla/dom/ProxyHandlerUtils.h" +#include "mozilla/dom/ToJSValue.h" +#include "mozilla/ErrorResult.h" +#include "nsDebug.h" +#include "nsJSUtils.h" + +namespace mozilla::dom { + +const char ObservableArrayProxyHandler::family = 0; + +bool ObservableArrayProxyHandler::defineProperty( + JSContext* aCx, JS::HandleObject aProxy, JS::HandleId aId, + JS::Handle aDesc, + JS::ObjectOpResult& aResult) const { + if (aId.get() == s_length_id) { + if (aDesc.isAccessorDescriptor()) { + return aResult.failNotDataDescriptor(); + } + if (aDesc.hasConfigurable() && aDesc.configurable()) { + return aResult.failInvalidDescriptor(); + } + if (aDesc.hasEnumerable() && aDesc.enumerable()) { + return aResult.failInvalidDescriptor(); + } + if (aDesc.hasWritable() && !aDesc.writable()) { + return aResult.failInvalidDescriptor(); + } + if (aDesc.hasValue()) { + JS::RootedObject backingListObj(aCx); + if (!GetBackingListObject(aCx, aProxy, &backingListObj)) { + return false; + } + + return SetLength(aCx, aProxy, backingListObj, aDesc.value(), aResult); + } + return aResult.succeed(); + } + uint32_t index = GetArrayIndexFromId(aId); + if (IsArrayIndex(index)) { + if (aDesc.isAccessorDescriptor()) { + return aResult.failNotDataDescriptor(); + } + if (aDesc.hasConfigurable() && !aDesc.configurable()) { + return aResult.failInvalidDescriptor(); + } + if (aDesc.hasEnumerable() && !aDesc.enumerable()) { + return aResult.failInvalidDescriptor(); + } + if (aDesc.hasWritable() && !aDesc.writable()) { + return aResult.failInvalidDescriptor(); + } + if (aDesc.hasValue()) { + JS::RootedObject backingListObj(aCx); + if (!GetBackingListObject(aCx, aProxy, &backingListObj)) { + return false; + } + + return SetIndexedValue(aCx, aProxy, backingListObj, index, aDesc.value(), + aResult); + } + return aResult.succeed(); + } + + return ForwardingProxyHandler::defineProperty(aCx, aProxy, aId, aDesc, + aResult); +} + +bool ObservableArrayProxyHandler::delete_(JSContext* aCx, + JS::HandleObject aProxy, + JS::HandleId aId, + JS::ObjectOpResult& aResult) const { + if (aId.get() == s_length_id) { + return aResult.failCantDelete(); + } + uint32_t index = GetArrayIndexFromId(aId); + if (IsArrayIndex(index)) { + JS::RootedObject backingListObj(aCx); + if (!GetBackingListObject(aCx, aProxy, &backingListObj)) { + return false; + } + + uint32_t oldLen = 0; + if (!JS::GetArrayLength(aCx, backingListObj, &oldLen)) { + return false; + } + + // We do not follow the spec (step 3.3 in + // https://webidl.spec.whatwg.org/#es-observable-array-deleteProperty) + // is because `oldLen - 1` could be `-1` if the backing list is empty, but + // `oldLen` is `uint32_t` in practice. See also + // https://github.com/whatwg/webidl/issues/1049. + if (oldLen != index + 1) { + return aResult.failBadIndex(); + } + + JS::RootedValue value(aCx); + if (!JS_GetElement(aCx, backingListObj, index, &value)) { + return false; + } + + if (!OnDeleteItem(aCx, aProxy, value, index)) { + return false; + } + + if (!JS::SetArrayLength(aCx, backingListObj, index)) { + return false; + } + + return aResult.succeed(); + } + return ForwardingProxyHandler::delete_(aCx, aProxy, aId, aResult); +} + +bool ObservableArrayProxyHandler::get(JSContext* aCx, JS::HandleObject aProxy, + JS::HandleValue aReceiver, + JS::HandleId aId, + JS::MutableHandleValue aVp) const { + JS::RootedObject backingListObj(aCx); + if (!GetBackingListObject(aCx, aProxy, &backingListObj)) { + return false; + } + + uint32_t length = 0; + if (!JS::GetArrayLength(aCx, backingListObj, &length)) { + return false; + } + + if (aId.get() == s_length_id) { + return ToJSValue(aCx, length, aVp); + } + uint32_t index = GetArrayIndexFromId(aId); + if (IsArrayIndex(index)) { + if (index >= length) { + aVp.setUndefined(); + return true; + } + return JS_GetElement(aCx, backingListObj, index, aVp); + } + return ForwardingProxyHandler::get(aCx, aProxy, aReceiver, aId, aVp); +} + +bool ObservableArrayProxyHandler::getOwnPropertyDescriptor( + JSContext* aCx, JS::HandleObject aProxy, JS::HandleId aId, + JS::MutableHandle> aDesc) const { + JS::RootedObject backingListObj(aCx); + if (!GetBackingListObject(aCx, aProxy, &backingListObj)) { + return false; + } + + uint32_t length = 0; + if (!JS::GetArrayLength(aCx, backingListObj, &length)) { + return false; + } + + if (aId.get() == s_length_id) { + JS::RootedValue value(aCx, JS::NumberValue(length)); + aDesc.set(Some(JS::PropertyDescriptor::Data( + value, {JS::PropertyAttribute::Writable}))); + return true; + } + uint32_t index = GetArrayIndexFromId(aId); + if (IsArrayIndex(index)) { + if (index >= length) { + return true; + } + + JS::RootedValue value(aCx); + if (!JS_GetElement(aCx, backingListObj, index, &value)) { + return false; + } + + aDesc.set(Some(JS::PropertyDescriptor::Data( + value, + {JS::PropertyAttribute::Configurable, JS::PropertyAttribute::Writable, + JS::PropertyAttribute::Enumerable}))); + return true; + } + return ForwardingProxyHandler::getOwnPropertyDescriptor(aCx, aProxy, aId, + aDesc); +} + +bool ObservableArrayProxyHandler::has(JSContext* aCx, JS::HandleObject aProxy, + JS::HandleId aId, bool* aBp) const { + if (aId.get() == s_length_id) { + *aBp = true; + return true; + } + uint32_t index = GetArrayIndexFromId(aId); + if (IsArrayIndex(index)) { + uint32_t length = 0; + if (!GetBackingListLength(aCx, aProxy, &length)) { + return false; + } + + *aBp = (index < length); + return true; + } + return ForwardingProxyHandler::has(aCx, aProxy, aId, aBp); +} + +bool ObservableArrayProxyHandler::ownPropertyKeys( + JSContext* aCx, JS::HandleObject aProxy, + JS::MutableHandleVector aProps) const { + uint32_t length = 0; + if (!GetBackingListLength(aCx, aProxy, &length)) { + return false; + } + + for (int32_t i = 0; i < int32_t(length); i++) { + if (!aProps.append(JS::PropertyKey::Int(i))) { + return false; + } + } + return ForwardingProxyHandler::ownPropertyKeys(aCx, aProxy, aProps); +} + +bool ObservableArrayProxyHandler::preventExtensions( + JSContext* aCx, JS::HandleObject aProxy, + JS::ObjectOpResult& aResult) const { + return aResult.failCantPreventExtensions(); +} + +bool ObservableArrayProxyHandler::set(JSContext* aCx, JS::HandleObject aProxy, + JS::HandleId aId, JS::HandleValue aV, + JS::HandleValue aReceiver, + JS::ObjectOpResult& aResult) const { + if (aId.get() == s_length_id) { + JS::RootedObject backingListObj(aCx); + if (!GetBackingListObject(aCx, aProxy, &backingListObj)) { + return false; + } + + return SetLength(aCx, aProxy, backingListObj, aV, aResult); + } + uint32_t index = GetArrayIndexFromId(aId); + if (IsArrayIndex(index)) { + JS::RootedObject backingListObj(aCx); + if (!GetBackingListObject(aCx, aProxy, &backingListObj)) { + return false; + } + + return SetIndexedValue(aCx, aProxy, backingListObj, index, aV, aResult); + } + return ForwardingProxyHandler::set(aCx, aProxy, aId, aV, aReceiver, aResult); +} + +bool ObservableArrayProxyHandler::GetBackingListObject( + JSContext* aCx, JS::HandleObject aProxy, + JS::MutableHandleObject aBackingListObject) const { + // Retrieve the backing list object from the reserved slot on the proxy + // object. If it doesn't exist yet, create it. + JS::RootedValue slotValue(aCx); + slotValue = js::GetProxyReservedSlot( + aProxy, OBSERVABLE_ARRAY_BACKING_LIST_OBJECT_SLOT); + if (slotValue.isUndefined()) { + JS::RootedObject newBackingListObj(aCx); + newBackingListObj.set(JS::NewArrayObject(aCx, 0)); + if (NS_WARN_IF(!newBackingListObj)) { + return false; + } + slotValue = JS::ObjectValue(*newBackingListObj); + js::SetProxyReservedSlot(aProxy, OBSERVABLE_ARRAY_BACKING_LIST_OBJECT_SLOT, + slotValue); + } + aBackingListObject.set(&slotValue.toObject()); + return true; +} + +bool ObservableArrayProxyHandler::GetBackingListLength( + JSContext* aCx, JS::HandleObject aProxy, uint32_t* aLength) const { + JS::RootedObject backingListObj(aCx); + if (!GetBackingListObject(aCx, aProxy, &backingListObj)) { + return false; + } + + return JS::GetArrayLength(aCx, backingListObj, aLength); +} + +bool ObservableArrayProxyHandler::SetLength(JSContext* aCx, + JS::HandleObject aProxy, + uint32_t aLength) const { + JS::RootedObject backingListObj(aCx); + if (!GetBackingListObject(aCx, aProxy, &backingListObj)) { + return false; + } + + JS::ObjectOpResult result; + if (!SetLength(aCx, aProxy, backingListObj, aLength, result)) { + return false; + } + + return result ? true : result.reportError(aCx, aProxy); +} + +bool ObservableArrayProxyHandler::SetLength(JSContext* aCx, + JS::HandleObject aProxy, + JS::HandleObject aBackingList, + uint32_t aLength, + JS::ObjectOpResult& aResult) const { + uint32_t oldLen; + if (!JS::GetArrayLength(aCx, aBackingList, &oldLen)) { + return false; + } + + if (aLength > oldLen) { + return aResult.failBadArrayLength(); + } + + bool ok = true; + uint32_t len = oldLen; + for (; len > aLength; len--) { + uint32_t indexToDelete = len - 1; + JS::RootedValue value(aCx); + if (!JS_GetElement(aCx, aBackingList, indexToDelete, &value)) { + ok = false; + break; + } + + if (!OnDeleteItem(aCx, aProxy, value, indexToDelete)) { + ok = false; + break; + } + } + + return JS::SetArrayLength(aCx, aBackingList, len) && ok ? aResult.succeed() + : false; +} + +bool ObservableArrayProxyHandler::SetLength(JSContext* aCx, + JS::HandleObject aProxy, + JS::HandleObject aBackingList, + JS::HandleValue aValue, + JS::ObjectOpResult& aResult) const { + uint32_t uint32Len; + if (!ToUint32(aCx, aValue, &uint32Len)) { + return false; + } + + double numberLen; + if (!ToNumber(aCx, aValue, &numberLen)) { + return false; + } + + if (uint32Len != numberLen) { + JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr, + JSMSG_BAD_INDEX); + return false; + } + + return SetLength(aCx, aProxy, aBackingList, uint32Len, aResult); +} + +} // namespace mozilla::dom diff --git a/dom/bindings/ObservableArrayProxyHandler.h b/dom/bindings/ObservableArrayProxyHandler.h new file mode 100644 index 000000000000..cf31dfc25ae5 --- /dev/null +++ b/dom/bindings/ObservableArrayProxyHandler.h @@ -0,0 +1,108 @@ +/* -*- 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_ObservableArrayProxyHandler_h +#define mozilla_dom_ObservableArrayProxyHandler_h + +#include "js/TypeDecls.h" +#include "js/Wrapper.h" + +namespace mozilla { +namespace dom { + +/** + * Proxy handler for observable array exotic object. + * + * The indexed properties are stored in the backing list object in reserved slot + * of the proxy object with special treatment intact. + * + * The additional properties are stored in the proxy target object. + */ + +class ObservableArrayProxyHandler : public js::ForwardingProxyHandler { + public: + explicit constexpr ObservableArrayProxyHandler() + : js::ForwardingProxyHandler(&family) {} + + // Implementations of methods that can be implemented in terms of + // other lower-level methods. + bool defineProperty(JSContext* aCx, JS::HandleObject aProxy, JS::HandleId aId, + JS::Handle aDesc, + JS::ObjectOpResult& aResult) const override; + + bool delete_(JSContext* aCx, JS::HandleObject aProxy, JS::HandleId aId, + JS::ObjectOpResult& aResult) const override; + + bool get(JSContext* aCx, JS::HandleObject aProxy, JS::HandleValue aReceiver, + JS::HandleId aId, JS::MutableHandleValue aVp) const override; + + bool getOwnPropertyDescriptor( + JSContext* aCx, JS::HandleObject aProxy, JS::HandleId aId, + JS::MutableHandle> aDesc) const override; + + bool has(JSContext* aCx, JS::HandleObject aProxy, JS::HandleId aId, + bool* aBp) const override; + + bool ownPropertyKeys(JSContext* aCx, JS::HandleObject aProxy, + JS::MutableHandleVector aProps) const override; + + bool preventExtensions(JSContext* aCx, JS::HandleObject aProxy, + JS::ObjectOpResult& aResult) const override; + + bool set(JSContext* aCx, JS::HandleObject aProxy, JS::HandleId aId, + JS::HandleValue aV, JS::HandleValue aReceiver, + JS::ObjectOpResult& aResult) const override; + + bool SetLength(JSContext* aCx, JS::HandleObject aProxy, + uint32_t aLength) const; + + static const char family; + + protected: + bool GetBackingListObject(JSContext* aCx, JS::HandleObject aProxy, + JS::MutableHandleObject aBackingListObject) const; + + bool GetBackingListLength(JSContext* aCx, JS::HandleObject aProxy, + uint32_t* aLength) const; + + bool SetLength(JSContext* aCx, JS::HandleObject aProxy, + JS::HandleObject aBackingList, uint32_t aLength, + JS::ObjectOpResult& aResult) const; + + bool SetLength(JSContext* aCx, JS::HandleObject aProxy, + JS::HandleObject aBackingList, JS::HandleValue aValue, + JS::ObjectOpResult& aResult) const; + + // Hook for subclasses to invoke the setting the indexed value steps which + // would invoke DeleteAlgorithm/SetAlgorithm defined and implemented per + // interface. Returns false and throw exception on failure. + virtual bool SetIndexedValue(JSContext* aCx, JS::HandleObject aProxy, + JS::HandleObject aBackingList, uint32_t aIndex, + JS::HandleValue aValue, + JS::ObjectOpResult& aResult) const = 0; + + // Hook for subclasses to invoke the DeleteAlgorithm defined and implemented + // per interface. Returns false and throw exception on failure. + virtual bool OnDeleteItem(JSContext* aCx, JS::HandleObject aProxy, + JS::HandleValue aValue, uint32_t aIndex) const = 0; +}; + +inline bool IsObservableArrayProxy(JSObject* obj) { + return js::IsProxy(obj) && js::GetProxyHandler(obj)->family() == + &ObservableArrayProxyHandler::family; +} + +inline const ObservableArrayProxyHandler* GetObservableArrayProxyHandler( + JSObject* obj) { + MOZ_ASSERT(IsObservableArrayProxy(obj)); + return static_cast( + js::GetProxyHandler(obj)); +} + +} // namespace dom +} // namespace mozilla + +#endif /* mozilla_dom_ObservableArrayProxyHandler_h */ diff --git a/dom/bindings/ProxyHandlerUtils.h b/dom/bindings/ProxyHandlerUtils.h new file mode 100644 index 000000000000..0babda280d99 --- /dev/null +++ b/dom/bindings/ProxyHandlerUtils.h @@ -0,0 +1,64 @@ +/* -*- 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_ProxyHandlerUtils_h +#define mozilla_dom_ProxyHandlerUtils_h + +#include "mozilla/Likely.h" +#include "mozilla/Maybe.h" +#include "mozilla/TextUtils.h" + +#include "js/Id.h" +#include "js/Object.h" // JS::GetClass +#include "js/PropertyDescriptor.h" +#include "js/String.h" // JS::AtomToLinearString, JS::GetLinearString{CharAt,Length} +#include "js/TypeDecls.h" + +#include "jsfriendapi.h" // js::StringIsArrayIndex + +namespace mozilla { +namespace dom { + +extern jsid s_length_id; + +// A return value of UINT32_MAX indicates "not an array index". Note, in +// particular, that UINT32_MAX itself is not a valid array index in general. +inline uint32_t GetArrayIndexFromId(JS::Handle id) { + // Much like js::IdIsIndex, except with a fast path for "length" and another + // fast path for starting with a lowercase ascii char. Is that second one + // really needed? I guess it is because StringIsArrayIndex is out of line... + // as of now, use id.get() instead of id otherwise operands mismatch error + // occurs. + if (MOZ_LIKELY(id.isInt())) { + return id.toInt(); + } + if (MOZ_LIKELY(id.get() == s_length_id)) { + return UINT32_MAX; + } + if (MOZ_UNLIKELY(!id.isAtom())) { + return UINT32_MAX; + } + + JSLinearString* str = JS::AtomToLinearString(id.toAtom()); + if (MOZ_UNLIKELY(JS::GetLinearStringLength(str) == 0)) { + return UINT32_MAX; + } + + char16_t firstChar = JS::GetLinearStringCharAt(str, 0); + if (MOZ_LIKELY(IsAsciiLowercaseAlpha(firstChar))) { + return UINT32_MAX; + } + + uint32_t i; + return js::StringIsArrayIndex(str, &i) ? i : UINT32_MAX; +} + +inline bool IsArrayIndex(uint32_t index) { return index < UINT32_MAX; } + +} // namespace dom +} // namespace mozilla + +#endif /* mozilla_dom_ProxyHandlerUtils_h */ diff --git a/dom/bindings/ToJSValue.h b/dom/bindings/ToJSValue.h index 42805febdf50..a087c6bb6e1e 100644 --- a/dom/bindings/ToJSValue.h +++ b/dom/bindings/ToJSValue.h @@ -275,6 +275,12 @@ template return ToJSValue(aCx, *aArgument.get(), aValue); } +template +[[nodiscard]] bool ToJSValue(JSContext* aCx, const OwningNonNull& aArgument, + JS::MutableHandle aValue) { + return ToJSValue(aCx, *aArgument.get(), aValue); +} + // Accept WebIDL dictionaries template [[nodiscard]] std::enable_if_t::value, bool> diff --git a/dom/bindings/WebIDLGlobalNameHash.cpp b/dom/bindings/WebIDLGlobalNameHash.cpp index 37022dcf462c..4ed6b96706db 100644 --- a/dom/bindings/WebIDLGlobalNameHash.cpp +++ b/dom/bindings/WebIDLGlobalNameHash.cpp @@ -18,10 +18,10 @@ #include "mozilla/Maybe.h" #include "mozilla/dom/BindingNames.h" #include "mozilla/dom/DOMJSClass.h" -#include "mozilla/dom/DOMJSProxyHandler.h" #include "mozilla/dom/Exceptions.h" #include "mozilla/dom/JSSlots.h" #include "mozilla/dom/PrototypeList.h" +#include "mozilla/dom/ProxyHandlerUtils.h" #include "mozilla/dom/RegisterBindings.h" #include "nsGlobalWindow.h" #include "nsTHashtable.h" diff --git a/dom/bindings/moz.build b/dom/bindings/moz.build index a5b33b7b721b..92273e06d36f 100644 --- a/dom/bindings/moz.build +++ b/dom/bindings/moz.build @@ -43,8 +43,10 @@ EXPORTS.mozilla.dom += [ "JSSlots.h", "NonRefcountedDOMObject.h", "Nullable.h", + "ObservableArrayProxyHandler.h", "PinnedStringId.h", "PrimitiveConversions.h", + "ProxyHandlerUtils.h", "Record.h", "RemoteObjectProxy.h", "RootedDictionary.h", @@ -116,6 +118,7 @@ UNIFIED_SOURCES += [ "IterableIterator.cpp", "nsScriptError.cpp", "nsScriptErrorWithStack.cpp", + "ObservableArrayProxyHandler.cpp", "RemoteObjectProxy.cpp", "SimpleGlobalObject.cpp", "ToJSValue.cpp", @@ -136,6 +139,7 @@ if CONFIG["MOZ_DEBUG"] and CONFIG["ENABLE_TESTS"]: "test/TestInterfaceMaplike.h", "test/TestInterfaceMaplikeJSObject.h", "test/TestInterfaceMaplikeObject.h", + "test/TestInterfaceObservableArray.h", "test/TestInterfaceSetlike.h", "test/TestInterfaceSetlikeNode.h", "test/TestTrialInterface.h", @@ -149,6 +153,7 @@ if CONFIG["MOZ_DEBUG"] and CONFIG["ENABLE_TESTS"]: "test/TestInterfaceMaplike.cpp", "test/TestInterfaceMaplikeJSObject.cpp", "test/TestInterfaceMaplikeObject.cpp", + "test/TestInterfaceObservableArray.cpp", "test/TestInterfaceSetlike.cpp", "test/TestInterfaceSetlikeNode.cpp", "test/TestTrialInterface.cpp", diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py index c1ac3d583553..2c6f108d29cd 100644 --- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -1034,6 +1034,14 @@ class IDLInterfaceOrNamespace(IDLInterfaceOrInterfaceMixinOrNamespace): % (member.maplikeOrSetlikeOrIterableType), [member.location], ) + if member.valueType.isObservableArray() or ( + member.hasKeyType() and member.keyType.isObservableArray() + ): + raise WebIDLError( + "%s declaration uses ObservableArray as value or key type" + % (member.maplikeOrSetlikeOrIterableType), + [member.location], + ) # Check that we only have one interface declaration (currently # there can only be one maplike/setlike declaration per # interface) @@ -1299,12 +1307,13 @@ class IDLInterfaceOrNamespace(IDLInterfaceOrInterfaceMixinOrNamespace): and ( member.getExtendedAttribute("StoreInSlot") or member.getExtendedAttribute("Cached") + or member.type.isObservableArray() ) ) or member.isMaplikeOrSetlike(): if self.isJSImplemented() and not member.isMaplikeOrSetlike(): raise WebIDLError( "Interface %s is JS-implemented and we " - "don't support [Cached] or [StoreInSlot] " + "don't support [Cached] or [StoreInSlot] or ObservableArray " "on JS-implemented interfaces" % self.identifier.name, [self.location, member.location], ) @@ -2351,6 +2360,7 @@ class IDLType(IDLObject): "sequence", "record", "promise", + "observablearray", ) def __init__(self, location, name): @@ -2498,6 +2508,9 @@ class IDLType(IDLObject): def isJSONType(self): return False + def isObservableArray(self): + return False + def hasClamp(self): return self._clamp @@ -2723,6 +2736,9 @@ class IDLNullableType(IDLParametrizedType): def isJSONType(self): return self.inner.isJSONType() + def isObservableArray(self): + return self.inner.isObservableArray() + def hasClamp(self): return self.inner.hasClamp() @@ -2745,7 +2761,7 @@ class IDLNullableType(IDLParametrizedType): if self.inner.nullable(): raise WebIDLError( - "The inner type of a nullable type must not be " "a nullable type", + "The inner type of a nullable type must not be a nullable type", [self.location, self.inner.location], ) if self.inner.isUnion(): @@ -2762,6 +2778,11 @@ class IDLNullableType(IDLParametrizedType): "[LegacyNullToEmptyString] not allowed on a nullable DOMString", [self.location, self.inner.location], ) + if self.inner.isObservableArray(): + raise WebIDLError( + "The inner type of a nullable type must not be an ObservableArray type", + [self.location, self.inner.location], + ) self.name = self.inner.name + "OrNull" return self @@ -2821,6 +2842,12 @@ class IDLSequenceType(IDLParametrizedType): return IDLType.Tags.sequence def complete(self, scope): + if self.inner.isObservableArray(): + raise WebIDLError( + "The inner type of a sequence type must not be an ObservableArray type", + [self.location, self.inner.location], + ) + self.inner = self.inner.complete(scope) self.name = self.inner.name + "Sequence" return self @@ -2878,6 +2905,12 @@ class IDLRecordType(IDLParametrizedType): return IDLType.Tags.record def complete(self, scope): + if self.inner.isObservableArray(): + raise WebIDLError( + "The value type of a record type must not be an ObservableArray type", + [self.location, self.inner.location], + ) + self.inner = self.inner.complete(scope) self.name = self.keyType.name + self.inner.name + "Record" return self @@ -2906,6 +2939,75 @@ class IDLRecordType(IDLParametrizedType): return self.inner.unroll().isExposedInAllOf(exposureSet) +class IDLObservableArrayType(IDLParametrizedType): + def __init__(self, location, innerType): + assert not innerType.isVoid() + IDLParametrizedType.__init__(self, location, None, innerType) + + def __hash__(self): + return hash(self.inner) + + def __eq__(self, other): + return isinstance(other, IDLObservableArrayType) and self.inner == other.inner + + def __str__(self): + return self.inner.__str__() + "ObservableArray" + + def prettyName(self): + return "ObservableArray<%s>" % self.inner.prettyName() + + def isVoid(self): + return False + + def isJSONType(self): + return self.inner.isJSONType() + + def isObservableArray(self): + return True + + def isComplete(self): + return self.name is not None + + def tag(self): + return IDLType.Tags.observablearray + + def complete(self, scope): + if not self.inner.isComplete(): + self.inner = self.inner.complete(scope) + assert self.inner.isComplete() + + if self.inner.isDictionary(): + raise WebIDLError( + "The inner type of an ObservableArray type must not " + "be a dictionary type", + [self.location, self.inner.location], + ) + if self.inner.isSequence(): + raise WebIDLError( + "The inner type of an ObservableArray type must not " + "be a sequence type", + [self.location, self.inner.location], + ) + if self.inner.isRecord(): + raise WebIDLError( + "The inner type of an ObservableArray type must not be a record type", + [self.location, self.inner.location], + ) + if self.inner.isObservableArray(): + raise WebIDLError( + "The inner type of an ObservableArray type must not " + "be an ObservableArray type", + [self.location, self.inner.location], + ) + + self.name = self.inner.name + "ObservableArray" + return self + + def isDistinguishableFrom(self, other): + # ObservableArrays are not distinguishable from anything. + return False + + class IDLUnionType(IDLType): def __init__(self, location, memberTypes): IDLType.__init__(self, location, "") @@ -3388,6 +3490,12 @@ class IDLPromiseType(IDLParametrizedType): return IDLType.Tags.promise def complete(self, scope): + if self.inner.isObservableArray(): + raise WebIDLError( + "The inner type of a promise type must not be an ObservableArray type", + [self.location, self.inner.location], + ) + self.inner = self.promiseInnerType().complete(scope) return self @@ -5064,6 +5172,21 @@ class IDLAttribute(IDLInterfaceMember): "Promise-returning attributes must be readonly", [self.location] ) + if self.type.isObservableArray(): + if self.isStatic(): + raise WebIDLError( + "A static attribute cannot have an ObservableArray type", + [self.location], + ) + if self.getExtendedAttribute("Cached") or self.getExtendedAttribute( + "StoreInSlot" + ): + raise WebIDLError( + "[Cached] and [StoreInSlot] must not be used " + "on an attribute whose type is ObservableArray", + [self.location], + ) + def validate(self): def typeContainsChromeOnlyDictionaryMember(type): if type.nullable() or type.isSequence() or type.isRecord(): @@ -5608,6 +5731,12 @@ class IDLArgument(IDLObjectWithIdentifier): "Dictionary members cannot be [LegacyNullToEmptyString]", [self.location], ) + if self.type.isObservableArray(): + raise WebIDLError( + "%s cannot have an ObservableArray type" + % ("Dictionary members" if self.dictionaryMember else "Arguments"), + [self.location], + ) # Now do the coercing thing; this needs to happen after the # above creation of a default value. if self.defaultValue: @@ -6692,6 +6821,7 @@ class Tokenizer(object): "float": "FLOAT", "long": "LONG", "object": "OBJECT", + "ObservableArray": "OBSERVABLEARRAY", "octet": "OCTET", "Promise": "PROMISE", "required": "REQUIRED", @@ -8231,6 +8361,14 @@ class Parser(Tokenizer): type = IDLRecordType(self.getLocation(p, 1), keyType, valueType) p[0] = self.handleNullable(type, p[7]) + def p_DistinguishableTypeObservableArrayType(self, p): + """ + DistinguishableType : OBSERVABLEARRAY LT TypeWithExtendedAttributes GT Null + """ + innerType = p[3] + type = IDLObservableArrayType(self.getLocation(p, 1), innerType) + p[0] = self.handleNullable(type, p[5]) + def p_DistinguishableTypeScopedName(self, p): """ DistinguishableType : ScopedName Null diff --git a/dom/bindings/parser/runtests.py b/dom/bindings/parser/runtests.py index 0204fc91a437..abf16a7b190c 100644 --- a/dom/bindings/parser/runtests.py +++ b/dom/bindings/parser/runtests.py @@ -3,7 +3,8 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. from __future__ import print_function -import os, sys +import os +import sys import glob import argparse import traceback @@ -53,6 +54,17 @@ class TestHarness(object): else: self.test_fail(msg + " | Got %s expected %s" % (a, b)) + def should_throw(self, parser, code, msg): + parser = parser.reset() + threw = False + try: + parser.parse(code) + parser.finish() + except: + threw = True + + self.ok(threw, "Should have thrown: %s" % msg) + def run_tests(tests, verbose): testdir = os.path.join(os.path.dirname(__file__), "tests") diff --git a/dom/bindings/parser/tests/test_observableArray.py b/dom/bindings/parser/tests/test_observableArray.py new file mode 100644 index 000000000000..3ddd6fe6f9d0 --- /dev/null +++ b/dom/bindings/parser/tests/test_observableArray.py @@ -0,0 +1,302 @@ +# 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/. + + +def WebIDLTest(parser, harness): + + # Test void as inner type + # `void` could only be used as return type or the parameter of a promise + # type. This test should no longer be valid after bug 1659158 that we change + # `void` to `undefined`. + harness.should_throw( + parser, + """ + interface A { + attribute ObservableArray foo; + }; + """, + "use void as inner type", + ) + + # Test dictionary as inner type + harness.should_throw( + parser, + """ + dictionary A { + boolean member; + }; + interface B { + attribute ObservableArray foo; + }; + """, + "use dictionary as inner type", + ) + + # Test sequence as inner type + harness.should_throw( + parser, + """ + interface A { + attribute ObservableArray> foo; + }; + """, + "use sequence as inner type", + ) + + # Test sequence as inner type + harness.should_throw( + parser, + """ + dictionary A { + boolean member; + }; + interface B { + attribute ObservableArray> foo; + }; + """, + "use sequence as inner type", + ) + + # Test record as inner type + harness.should_throw( + parser, + """ + interface A { + attribute ObservableArray> foo; + }; + """, + "use record as inner type", + ) + + # Test record as inner type + harness.should_throw( + parser, + """ + dictionary A { + boolean member; + }; + interface B { + attribute ObservableArray> foo; + }; + """, + "use record as inner type", + ) + + # Test observable array as inner type + harness.should_throw( + parser, + """ + interface A { + attribute ObservableArray> foo; + }; + """, + "use ObservableArray as inner type", + ) + + # Test nullable attribute + harness.should_throw( + parser, + """ + interface A { + attribute ObservableArray? foo; + }; + """, + "nullable", + ) + + # Test sequence + harness.should_throw( + parser, + """ + interface A { + void foo(sequence> foo); + }; + """, + "used in sequence", + ) + + # Test record + harness.should_throw( + parser, + """ + interface A { + void foo(record> foo); + }; + """, + "used in record", + ) + + # Test promise + harness.should_throw( + parser, + """ + interface A { + Promise> foo(); + }; + """, + "used in promise", + ) + + # Test union + harness.should_throw( + parser, + """ + interface A { + attribute (DOMString or ObservableArray>) foo; + }; + """, + "used in union", + ) + + # Test dictionary member + harness.should_throw( + parser, + """ + dictionary A { + ObservableArray foo; + }; + """, + "used on dictionary member type", + ) + + # Test argument + harness.should_throw( + parser, + """ + interface A { + void foo(ObservableArray foo); + }; + """, + "used on argument", + ) + + # Test static attribute + harness.should_throw( + parser, + """ + interface A { + static attribute ObservableArray foo; + }; + """, + "used on static attribute type", + ) + + # Test iterable + harness.should_throw( + parser, + """ + interface A { + iterable>; + }; + """, + "used in iterable", + ) + + # Test maplike + harness.should_throw( + parser, + """ + interface A { + maplike>; + }; + """, + "used in maplike", + ) + + # Test setlike + harness.should_throw( + parser, + """ + interface A { + setlike>; + }; + """, + "used in setlike", + ) + + # Test JS implemented interface + harness.should_throw( + parser, + """ + [JSImplementation="@mozilla.org/dom/test-interface-js;1"] + interface A { + readonly attribute ObservableArray foo; + }; + """, + "used in JS implemented interface", + ) + + # Test namespace + harness.should_throw( + parser, + """ + namespace A { + readonly attribute ObservableArray foo; + }; + """, + "used in namespaces", + ) + + # Test [Cached] extended attribute + harness.should_throw( + parser, + """ + interface A { + [Cached, Pure] + readonly attribute ObservableArray foo; + }; + """, + "have Cached extended attribute", + ) + + # Test [StoreInSlot] extended attribute + harness.should_throw( + parser, + """ + interface A { + [StoreInSlot, Pure] + readonly attribute ObservableArray foo; + }; + """, + "have StoreInSlot extended attribute", + ) + + # Test regular attribute + parser = parser.reset() + parser.parse( + """ + interface A { + readonly attribute ObservableArray foo; + attribute ObservableArray<[Clamp] octet> bar; + attribute ObservableArray baz; + attribute ObservableArray<(boolean or long)> qux; + }; + """ + ) + results = parser.finish() + A = results[0] + foo = A.members[0] + harness.ok(foo.readonly, "A.foo is readonly attribute") + harness.ok(foo.type.isObservableArray(), "A.foo is ObservableArray type") + harness.check( + foo.slotIndices[A.identifier.name], 0, "A.foo should be stored in slot" + ) + bar = A.members[1] + harness.ok(bar.type.isObservableArray(), "A.bar is ObservableArray type") + harness.check( + bar.slotIndices[A.identifier.name], 1, "A.bar should be stored in slot" + ) + harness.ok(bar.type.inner.hasClamp(), "A.bar's inner type should be clamped") + baz = A.members[2] + harness.ok(baz.type.isObservableArray(), "A.baz is ObservableArray type") + harness.check( + baz.slotIndices[A.identifier.name], 2, "A.baz should be stored in slot" + ) + harness.ok(baz.type.inner.nullable(), "A.baz's inner type should be nullable") + qux = A.members[3] + harness.ok(qux.type.isObservableArray(), "A.qux is ObservableArray type") + harness.check( + qux.slotIndices[A.identifier.name], 3, "A.qux should be stored in slot" + ) + harness.ok(qux.type.inner.isUnion(), "A.qux's inner type should be union") diff --git a/dom/bindings/test/TestBindingHeader.h b/dom/bindings/test/TestBindingHeader.h index 10ec474880a9..b1d9f7281e36 100644 --- a/dom/bindings/test/TestBindingHeader.h +++ b/dom/bindings/test/TestBindingHeader.h @@ -848,6 +848,24 @@ class TestInterface : public nsISupports, public nsWrapperCache { Promise* ReceivePromise(); already_AddRefed ReceiveAddrefedPromise(); + // ObservableArray types + void OnDeleteBooleanObservableArray(bool, uint32_t, ErrorResult&); + void OnSetBooleanObservableArray(bool, uint32_t, ErrorResult&); + void OnDeleteObjectObservableArray(JSContext*, JS::Handle, + uint32_t, ErrorResult&); + void OnSetObjectObservableArray(JSContext*, JS::Handle, uint32_t, + ErrorResult&); + void OnDeleteAnyObservableArray(JSContext*, JS::Handle, uint32_t, + ErrorResult&); + void OnSetAnyObservableArray(JSContext*, JS::Handle, uint32_t, + ErrorResult&); + void OnDeleteInterfaceObservableArray(TestInterface*, uint32_t, ErrorResult&); + void OnSetInterfaceObservableArray(TestInterface*, uint32_t, ErrorResult&); + void OnDeleteNullableObservableArray(const Nullable&, uint32_t, + ErrorResult&); + void OnSetNullableObservableArray(const Nullable&, uint32_t, + ErrorResult&); + // binaryNames tests void MethodRenamedTo(); void MethodRenamedTo(int8_t); diff --git a/dom/bindings/test/TestCodeGen.webidl b/dom/bindings/test/TestCodeGen.webidl index bc12ddd6d505..30841d1b7900 100644 --- a/dom/bindings/test/TestCodeGen.webidl +++ b/dom/bindings/test/TestCodeGen.webidl @@ -790,6 +790,13 @@ interface TestInterface { Promise receivePromise(); Promise receiveAddrefedPromise(); + // ObservableArray types + attribute ObservableArray booleanObservableArray; + attribute ObservableArray objectObservableArray; + attribute ObservableArray anyObservableArray; + attribute ObservableArray interfaceObservableArray; + attribute ObservableArray nullableObservableArray; + // binaryNames tests [BinaryName="methodRenamedTo"] void methodRenamedFrom(); diff --git a/dom/bindings/test/TestExampleGen.webidl b/dom/bindings/test/TestExampleGen.webidl index 6012fcebd878..dc07551a0fa6 100644 --- a/dom/bindings/test/TestExampleGen.webidl +++ b/dom/bindings/test/TestExampleGen.webidl @@ -591,6 +591,13 @@ interface TestExampleInterface { Promise receivePromise(); Promise receiveAddrefedPromise(); + // ObservableArray types + attribute ObservableArray booleanObservableArray; + attribute ObservableArray objectObservableArray; + attribute ObservableArray anyObservableArray; + attribute ObservableArray interfaceObservableArray; + attribute ObservableArray nullableObservableArray; + // binaryNames tests [BinaryName="methodRenamedTo"] void methodRenamedFrom(); diff --git a/dom/bindings/test/TestInterfaceObservableArray.cpp b/dom/bindings/test/TestInterfaceObservableArray.cpp new file mode 100644 index 000000000000..cb8d532eb86c --- /dev/null +++ b/dom/bindings/test/TestInterfaceObservableArray.cpp @@ -0,0 +1,225 @@ +/* -*- 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 "mozilla/dom/TestInterfaceObservableArray.h" +#include "mozilla/dom/TestInterfaceObservableArrayBinding.h" +#include "nsPIDOMWindow.h" +#include "mozilla/dom/BindingUtils.h" + +namespace mozilla::dom { + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TestInterfaceObservableArray, mParent, + mSetBooleanCallback, + mDeleteBooleanCallback, + mSetObjectCallback, mDeleteObjectCallback, + mSetInterfaceCallback, + mDeleteInterfaceCallback) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(TestInterfaceObservableArray) +NS_IMPL_CYCLE_COLLECTING_RELEASE(TestInterfaceObservableArray) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TestInterfaceObservableArray) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +TestInterfaceObservableArray::TestInterfaceObservableArray( + nsPIDOMWindowInner* aParent, const ObservableArrayCallbacks& aCallbacks) + : mParent(aParent) { + if (aCallbacks.mSetBooleanCallback.WasPassed()) { + mSetBooleanCallback = &aCallbacks.mSetBooleanCallback.Value(); + } + if (aCallbacks.mDeleteBooleanCallback.WasPassed()) { + mDeleteBooleanCallback = &aCallbacks.mDeleteBooleanCallback.Value(); + } + if (aCallbacks.mSetObjectCallback.WasPassed()) { + mSetObjectCallback = &aCallbacks.mSetObjectCallback.Value(); + } + if (aCallbacks.mDeleteObjectCallback.WasPassed()) { + mDeleteObjectCallback = &aCallbacks.mDeleteObjectCallback.Value(); + } + if (aCallbacks.mSetInterfaceCallback.WasPassed()) { + mSetInterfaceCallback = &aCallbacks.mSetInterfaceCallback.Value(); + } + if (aCallbacks.mDeleteInterfaceCallback.WasPassed()) { + mDeleteInterfaceCallback = &aCallbacks.mDeleteInterfaceCallback.Value(); + } +} + +// static +already_AddRefed +TestInterfaceObservableArray::Constructor( + const GlobalObject& aGlobal, const ObservableArrayCallbacks& aCallbacks, + ErrorResult& aRv) { + nsCOMPtr window = + do_QueryInterface(aGlobal.GetAsSupports()); + if (!window) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + RefPtr r = + new TestInterfaceObservableArray(window, aCallbacks); + return r.forget(); +} + +JSObject* TestInterfaceObservableArray::WrapObject( + JSContext* aCx, JS::Handle aGivenProto) { + return TestInterfaceObservableArray_Binding::Wrap(aCx, this, aGivenProto); +} + +nsPIDOMWindowInner* TestInterfaceObservableArray::GetParentObject() const { + return mParent; +} + +void TestInterfaceObservableArray::OnSetObservableArrayObject( + JSContext* aCx, JS::Handle aValue, uint32_t aIndex, + ErrorResult& aRv) { + if (mSetObjectCallback) { + MOZ_KnownLive(mSetObjectCallback) + ->Call(aValue, aIndex, aRv, "OnSetObservableArrayObject", + CallbackFunction::eRethrowExceptions); + } +} + +void TestInterfaceObservableArray::OnDeleteObservableArrayObject( + JSContext* aCx, JS::Handle aValue, uint32_t aIndex, + ErrorResult& aRv) { + if (mDeleteObjectCallback) { + MOZ_KnownLive(mDeleteObjectCallback) + ->Call(aValue, aIndex, aRv, "OnDeleteObservableArrayObject", + CallbackFunction::eRethrowExceptions); + } +} + +void TestInterfaceObservableArray::OnSetObservableArrayBoolean( + bool aValue, uint32_t aIndex, ErrorResult& aRv) { + if (mSetBooleanCallback) { + MOZ_KnownLive(mSetBooleanCallback) + ->Call(aValue, aIndex, aRv, "OnSetObservableArrayBoolean", + CallbackFunction::eRethrowExceptions); + } +} + +void TestInterfaceObservableArray::OnDeleteObservableArrayBoolean( + bool aValue, uint32_t aIndex, ErrorResult& aRv) { + if (mDeleteBooleanCallback) { + MOZ_KnownLive(mDeleteBooleanCallback) + ->Call(aValue, aIndex, aRv, "OnDeleteObservableArrayBoolean", + CallbackFunction::eRethrowExceptions); + } +} + +void TestInterfaceObservableArray::OnSetObservableArrayInterface( + TestInterfaceObservableArray* aValue, uint32_t aIndex, ErrorResult& aRv) { + if (mSetInterfaceCallback && aValue) { + MOZ_KnownLive(mSetInterfaceCallback) + ->Call(*aValue, aIndex, aRv, "OnSetObservableArrayInterface", + CallbackFunction::eRethrowExceptions); + } +} + +void TestInterfaceObservableArray::OnDeleteObservableArrayInterface( + TestInterfaceObservableArray* aValue, uint32_t aIndex, ErrorResult& aRv) { + if (mDeleteInterfaceCallback && aValue) { + MOZ_KnownLive(mDeleteInterfaceCallback) + ->Call(*aValue, aIndex, aRv, "OnDeleteObservableArrayInterface", + CallbackFunction::eRethrowExceptions); + } +} + +bool TestInterfaceObservableArray::BooleanElementAtInternal(uint32_t aIndex, + ErrorResult& aRv) { + return TestInterfaceObservableArray_Binding::ObservableArrayBooleanHelpers:: + ElementAt(this, aIndex, aRv); +} + +void TestInterfaceObservableArray::ObjectElementAtInternal( + JSContext* aCx, uint32_t aIndex, JS::MutableHandle aValue, + ErrorResult& aRv) { + TestInterfaceObservableArray_Binding::ObservableArrayObjectHelpers::ElementAt( + this, aCx, aIndex, aValue, aRv); +} + +already_AddRefed +TestInterfaceObservableArray::InterfaceElementAtInternal(uint32_t aIndex, + ErrorResult& aRv) { + return TestInterfaceObservableArray_Binding::ObservableArrayInterfaceHelpers:: + ElementAt(this, aIndex, aRv); +} + +void TestInterfaceObservableArray::BooleanReplaceElementAtInternal( + uint32_t aIndex, bool aValue, ErrorResult& aRv) { + TestInterfaceObservableArray_Binding::ObservableArrayBooleanHelpers:: + ReplaceElementAt(this, aIndex, aValue, aRv); +} + +void TestInterfaceObservableArray::ObjectReplaceElementAtInternal( + JSContext* aCx, uint32_t aIndex, JS::Handle aValue, + ErrorResult& aRv) { + TestInterfaceObservableArray_Binding::ObservableArrayObjectHelpers:: + ReplaceElementAt(this, aIndex, aValue, aRv); +} + +void TestInterfaceObservableArray::InterfaceReplaceElementAtInternal( + uint32_t aIndex, TestInterfaceObservableArray& aValue, ErrorResult& aRv) { + TestInterfaceObservableArray_Binding::ObservableArrayInterfaceHelpers:: + ReplaceElementAt(this, aIndex, aValue, aRv); +} + +void TestInterfaceObservableArray::BooleanAppendElementInternal( + bool aValue, ErrorResult& aRv) { + TestInterfaceObservableArray_Binding::ObservableArrayBooleanHelpers:: + AppendElement(this, aValue, aRv); +} + +void TestInterfaceObservableArray::ObjectAppendElementInternal( + JSContext* aCx, JS::Handle aValue, ErrorResult& aRv) { + TestInterfaceObservableArray_Binding::ObservableArrayObjectHelpers:: + AppendElement(this, aValue, aRv); +} + +void TestInterfaceObservableArray::InterfaceAppendElementInternal( + TestInterfaceObservableArray& aValue, ErrorResult& aRv) { + TestInterfaceObservableArray_Binding::ObservableArrayInterfaceHelpers:: + AppendElement(this, aValue, aRv); +} + +void TestInterfaceObservableArray::BooleanRemoveLastElementInternal( + ErrorResult& aRv) { + TestInterfaceObservableArray_Binding::ObservableArrayBooleanHelpers:: + RemoveLastElement(this, aRv); +} + +void TestInterfaceObservableArray::ObjectRemoveLastElementInternal( + ErrorResult& aRv) { + TestInterfaceObservableArray_Binding::ObservableArrayObjectHelpers:: + RemoveLastElement(this, aRv); +} + +void TestInterfaceObservableArray::InterfaceRemoveLastElementInternal( + ErrorResult& aRv) { + TestInterfaceObservableArray_Binding::ObservableArrayInterfaceHelpers:: + RemoveLastElement(this, aRv); +} + +uint32_t TestInterfaceObservableArray::BooleanLengthInternal(ErrorResult& aRv) { + return TestInterfaceObservableArray_Binding::ObservableArrayBooleanHelpers:: + Length(this, aRv); +} + +uint32_t TestInterfaceObservableArray::ObjectLengthInternal(ErrorResult& aRv) { + return TestInterfaceObservableArray_Binding::ObservableArrayObjectHelpers:: + Length(this, aRv); +} + +uint32_t TestInterfaceObservableArray::InterfaceLengthInternal( + ErrorResult& aRv) { + return TestInterfaceObservableArray_Binding::ObservableArrayInterfaceHelpers:: + Length(this, aRv); +} + +} // namespace mozilla::dom diff --git a/dom/bindings/test/TestInterfaceObservableArray.h b/dom/bindings/test/TestInterfaceObservableArray.h new file mode 100644 index 000000000000..810eeb08021b --- /dev/null +++ b/dom/bindings/test/TestInterfaceObservableArray.h @@ -0,0 +1,115 @@ +/* -*- 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_TestInterfaceObservableArray_h +#define mozilla_dom_TestInterfaceObservableArray_h + +#include "nsCOMPtr.h" +#include "nsWrapperCache.h" +#include "nsTArray.h" + +class nsPIDOMWindowInner; + +namespace mozilla { + +class ErrorResult; + +namespace dom { + +class GlobalObject; +class SetDeleteBooleanCallback; +class SetDeleteInterfaceCallback; +class SetDeleteObjectCallback; +struct ObservableArrayCallbacks; + +// Implementation of test binding for webidl ObservableArray type, using +// primitives for value type +class TestInterfaceObservableArray final : public nsISupports, + public nsWrapperCache { + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TestInterfaceObservableArray) + + nsPIDOMWindowInner* GetParentObject() const; + virtual JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override; + static already_AddRefed Constructor( + const GlobalObject& aGlobal, const ObservableArrayCallbacks& aCallbacks, + ErrorResult& rv); + + MOZ_CAN_RUN_SCRIPT_BOUNDARY + void OnSetObservableArrayObject(JSContext* aCx, JS::Handle aValue, + uint32_t aIndex, ErrorResult& aRv); + + MOZ_CAN_RUN_SCRIPT_BOUNDARY + void OnDeleteObservableArrayObject(JSContext* aCx, + JS::Handle aValue, + uint32_t aIndex, ErrorResult& aRv); + + MOZ_CAN_RUN_SCRIPT_BOUNDARY + void OnSetObservableArrayBoolean(bool aValue, uint32_t aIndex, + ErrorResult& aRv); + + MOZ_CAN_RUN_SCRIPT_BOUNDARY + void OnDeleteObservableArrayBoolean(bool aValue, uint32_t aIndex, + ErrorResult& aRv); + + MOZ_CAN_RUN_SCRIPT_BOUNDARY + void OnSetObservableArrayInterface(TestInterfaceObservableArray* aValue, + uint32_t aIndex, ErrorResult& aRv); + + MOZ_CAN_RUN_SCRIPT_BOUNDARY + void OnDeleteObservableArrayInterface(TestInterfaceObservableArray* aValue, + uint32_t aIndex, ErrorResult& aRv); + + bool BooleanElementAtInternal(uint32_t aIndex, ErrorResult& aRv); + void ObjectElementAtInternal(JSContext* aCx, uint32_t aIndex, + JS::MutableHandle aValue, + ErrorResult& aRv); + already_AddRefed InterfaceElementAtInternal( + uint32_t aIndex, ErrorResult& aRv); + + void BooleanReplaceElementAtInternal(uint32_t aIndex, bool aValue, + ErrorResult& aRv); + void ObjectReplaceElementAtInternal(JSContext* aCx, uint32_t aIndex, + JS::Handle aValue, + ErrorResult& aRv); + void InterfaceReplaceElementAtInternal(uint32_t aIndex, + TestInterfaceObservableArray& aValue, + ErrorResult& aRv); + + void BooleanAppendElementInternal(bool aValue, ErrorResult& aRv); + void ObjectAppendElementInternal(JSContext* aCx, JS::Handle aValue, + ErrorResult& aRv); + void InterfaceAppendElementInternal(TestInterfaceObservableArray& aValue, + ErrorResult& aRv); + + void BooleanRemoveLastElementInternal(ErrorResult& aRv); + void ObjectRemoveLastElementInternal(ErrorResult& aRv); + void InterfaceRemoveLastElementInternal(ErrorResult& aRv); + + uint32_t BooleanLengthInternal(ErrorResult& aRv); + uint32_t ObjectLengthInternal(ErrorResult& aRv); + uint32_t InterfaceLengthInternal(ErrorResult& aRv); + + private: + explicit TestInterfaceObservableArray( + nsPIDOMWindowInner* aParent, const ObservableArrayCallbacks& aCallbacks); + virtual ~TestInterfaceObservableArray() = default; + + nsCOMPtr mParent; + RefPtr mSetBooleanCallback; + RefPtr mDeleteBooleanCallback; + RefPtr mSetObjectCallback; + RefPtr mDeleteObjectCallback; + RefPtr mSetInterfaceCallback; + RefPtr mDeleteInterfaceCallback; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_TestInterfaceObservableArray_h diff --git a/dom/bindings/test/mochitest.ini b/dom/bindings/test/mochitest.ini index 662e5d3812cd..1c1d51cee000 100644 --- a/dom/bindings/test/mochitest.ini +++ b/dom/bindings/test/mochitest.ini @@ -92,4 +92,10 @@ skip-if = debug == false skip-if = debug == false [test_large_arraybuffers.html] skip-if = (debug == false || bits == 32) # Large ArrayBuffers are only supported on 64-bit platforms. +[test_observablearray.html] +skip-if = debug == false +[test_observablearray_proxyhandler.html] +skip-if = debug == false +[test_observablearray_helper.html] +skip-if = debug == false [test_large_imageData.html] diff --git a/dom/bindings/test/test_observablearray.html b/dom/bindings/test/test_observablearray.html new file mode 100644 index 000000000000..60501df03f01 --- /dev/null +++ b/dom/bindings/test/test_observablearray.html @@ -0,0 +1,523 @@ + + + + +Test Observable Array Type + + + + + + + diff --git a/dom/bindings/test/test_observablearray_helper.html b/dom/bindings/test/test_observablearray_helper.html new file mode 100644 index 000000000000..d2b4897cac7c --- /dev/null +++ b/dom/bindings/test/test_observablearray_helper.html @@ -0,0 +1,376 @@ + + + + +Test Helpers of Observable Array Type + + + + + + + diff --git a/dom/bindings/test/test_observablearray_proxyhandler.html b/dom/bindings/test/test_observablearray_proxyhandler.html new file mode 100644 index 000000000000..d7d8810981d9 --- /dev/null +++ b/dom/bindings/test/test_observablearray_proxyhandler.html @@ -0,0 +1,859 @@ + + + + +Test Observable Array Type + + + + + + + diff --git a/dom/html/TextControlState.cpp b/dom/html/TextControlState.cpp index 249d0d57aecc..3579ff53e088 100644 --- a/dom/html/TextControlState.cpp +++ b/dom/html/TextControlState.cpp @@ -2334,7 +2334,8 @@ void TextControlState::SetRangeText(const nsAString& aReplacement, Selection* selection = mSelCon ? mSelCon->GetSelection(SelectionType::eNormal) : nullptr; SelectionBatcher selectionBatcher( - selection, nsISelectionListener::JS_REASON); // no-op if nullptr + selection, __FUNCTION__, + nsISelectionListener::JS_REASON); // no-op if nullptr MOZ_ASSERT(aStart <= aEnd); value.Replace(aStart, aEnd - aStart, aReplacement); @@ -2787,7 +2788,7 @@ bool TextControlState::SetValueWithTextEditor( // FYI: It's safe to use raw pointer for selection here because // SelectionBatcher will grab it with RefPtr. Selection* selection = mSelCon->GetSelection(SelectionType::eNormal); - SelectionBatcher selectionBatcher(selection); + SelectionBatcher selectionBatcher(selection, __FUNCTION__); // get the flags, remove readonly, disabled and max-length, // set the value, restore flags diff --git a/dom/webidl/TestInterfaceObservableArray.webidl b/dom/webidl/TestInterfaceObservableArray.webidl new file mode 100644 index 000000000000..7137b2d30014 --- /dev/null +++ b/dom/webidl/TestInterfaceObservableArray.webidl @@ -0,0 +1,66 @@ +/* -*- 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/. + */ + +callback SetDeleteObjectCallback = void (object value, unsigned long index); +callback SetDeleteBooleanCallback = void (boolean value, unsigned long index); +callback SetDeleteInterfaceCallback = void (TestInterfaceObservableArray value, unsigned long index); + +dictionary ObservableArrayCallbacks { + SetDeleteObjectCallback setObjectCallback; + SetDeleteObjectCallback deleteObjectCallback; + SetDeleteBooleanCallback setBooleanCallback; + SetDeleteBooleanCallback deleteBooleanCallback; + SetDeleteInterfaceCallback setInterfaceCallback; + SetDeleteInterfaceCallback deleteInterfaceCallback; +}; + +[Pref="dom.expose_test_interfaces", + Exposed=Window] +interface TestInterfaceObservableArray { + [Throws] + constructor(optional ObservableArrayCallbacks callbacks = {}); + + // Testing for ObservableArray + attribute ObservableArray observableArrayBoolean; + attribute ObservableArray observableArrayObject; + attribute ObservableArray observableArrayInterface; + + // Tests for C++ helper function + [Throws] + boolean booleanElementAtInternal(unsigned long index); + [Throws] + TestInterfaceObservableArray interfaceElementAtInternal(unsigned long index); + [Throws] + object objectElementAtInternal(unsigned long index); + + [Throws] + void booleanReplaceElementAtInternal(unsigned long index, boolean value); + [Throws] + void interfaceReplaceElementAtInternal(unsigned long index, TestInterfaceObservableArray value); + [Throws] + void objectReplaceElementAtInternal(unsigned long index, object value); + + [Throws] + void booleanAppendElementInternal(boolean value); + [Throws] + void interfaceAppendElementInternal(TestInterfaceObservableArray value); + [Throws] + void objectAppendElementInternal(object value); + + [Throws] + void booleanRemoveLastElementInternal(); + [Throws] + void interfaceRemoveLastElementInternal(); + [Throws] + void objectRemoveLastElementInternal(); + + [Throws] + unsigned long booleanLengthInternal(); + [Throws] + unsigned long interfaceLengthInternal(); + [Throws] + unsigned long objectLengthInternal(); +}; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 5dcaf3916816..5feac0f9cf4f 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -1107,6 +1107,7 @@ if CONFIG["MOZ_DEBUG"] and CONFIG["ENABLE_TESTS"]: "TestInterfaceJS.webidl", "TestInterfaceJSDictionaries.webidl", "TestInterfaceJSMaplikeSetlikeIterable.webidl", + "TestInterfaceObservableArray.webidl", ] WEBIDL_FILES += [ diff --git a/editor/libeditor/CompositionTransaction.cpp b/editor/libeditor/CompositionTransaction.cpp index 0f9fb12f7e3b..af9ae4148af0 100644 --- a/editor/libeditor/CompositionTransaction.cpp +++ b/editor/libeditor/CompositionTransaction.cpp @@ -288,7 +288,7 @@ nsresult CompositionTransaction::SetIMESelection( return NS_ERROR_NOT_INITIALIZED; } - SelectionBatcher selectionBatcher(selection); + SelectionBatcher selectionBatcher(selection, __FUNCTION__); // First, remove all selections of IME composition. static const RawSelectionType kIMESelections[] = { diff --git a/editor/libeditor/EditorBase.cpp b/editor/libeditor/EditorBase.cpp index 53f54d16ad1e..44debb13d6c8 100644 --- a/editor/libeditor/EditorBase.cpp +++ b/editor/libeditor/EditorBase.cpp @@ -908,7 +908,7 @@ nsresult EditorBase::DoTransactionInternal(nsITransaction* aTransaction) { // XXX: re-entry during initial reflow. - kin // get the selection and start a batch change - SelectionBatcher selectionBatcher(SelectionRef()); + SelectionBatcher selectionBatcher(SelectionRef(), __FUNCTION__); if (mTransactionManager) { RefPtr transactionManager(mTransactionManager); @@ -1016,7 +1016,7 @@ nsresult EditorBase::UndoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) { return EditorBase::ToGenericNSResult(rv); } - AutoUpdateViewBatch preventSelectionChangeEvent(*this); + AutoUpdateViewBatch preventSelectionChangeEvent(*this, __FUNCTION__); NotifyEditorObservers(eNotifyEditorObserversOfBefore); if (NS_WARN_IF(!CanUndo()) || NS_WARN_IF(Destroyed())) { @@ -1081,7 +1081,7 @@ nsresult EditorBase::RedoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) { return EditorBase::ToGenericNSResult(rv); } - AutoUpdateViewBatch preventSelectionChangeEvent(*this); + AutoUpdateViewBatch preventSelectionChangeEvent(*this, __FUNCTION__); NotifyEditorObservers(eNotifyEditorObserversOfBefore); if (NS_WARN_IF(!CanRedo()) || NS_WARN_IF(Destroyed())) { @@ -1124,12 +1124,12 @@ NS_IMETHODIMP EditorBase::BeginTransaction() { return NS_ERROR_FAILURE; } - BeginTransactionInternal(); + BeginTransactionInternal(__FUNCTION__); return NS_OK; } -void EditorBase::BeginTransactionInternal() { - BeginUpdateViewBatch(); +void EditorBase::BeginTransactionInternal(const char* aRequesterFuncName) { + BeginUpdateViewBatch(aRequesterFuncName); if (NS_WARN_IF(!mTransactionManager)) { return; @@ -1147,11 +1147,11 @@ NS_IMETHODIMP EditorBase::EndTransaction() { return NS_ERROR_FAILURE; } - EndTransactionInternal(); + EndTransactionInternal(__FUNCTION__); return NS_OK; } -void EditorBase::EndTransactionInternal() { +void EditorBase::EndTransactionInternal(const char* aRequesterFuncName) { if (mTransactionManager) { RefPtr transactionManager(mTransactionManager); DebugOnly rvIgnored = transactionManager->EndBatch(false); @@ -1159,17 +1159,18 @@ void EditorBase::EndTransactionInternal() { "TransactionManager::EndBatch() failed, but ignored"); } - EndUpdateViewBatch(); + EndUpdateViewBatch(aRequesterFuncName); } -void EditorBase::BeginPlaceholderTransaction(nsStaticAtom& aTransactionName) { +void EditorBase::BeginPlaceholderTransaction(nsStaticAtom& aTransactionName, + const char* aRequesterFuncName) { MOZ_ASSERT(IsEditActionDataAvailable()); MOZ_ASSERT(mPlaceholderBatch >= 0, "negative placeholder batch count!"); if (!mPlaceholderBatch) { NotifyEditorObservers(eNotifyEditorObserversOfBefore); // time to turn on the batch - BeginUpdateViewBatch(); + BeginUpdateViewBatch(aRequesterFuncName); mPlaceholderTransaction = nullptr; mPlaceholderName = &aTransactionName; mSelState.emplace(); @@ -1187,7 +1188,8 @@ void EditorBase::BeginPlaceholderTransaction(nsStaticAtom& aTransactionName) { } void EditorBase::EndPlaceholderTransaction( - ScrollSelectionIntoView aScrollSelectionIntoView) { + ScrollSelectionIntoView aScrollSelectionIntoView, + const char* aRequesterFuncName) { MOZ_ASSERT(IsEditActionDataAvailable()); MOZ_ASSERT(mPlaceholderBatch > 0, "zero or negative placeholder batch count when ending batch!"); @@ -1202,7 +1204,7 @@ void EditorBase::EndPlaceholderTransaction( SelectionRef().SetCanCacheFrameOffset(true); // time to turn off the batch - EndUpdateViewBatch(); + EndUpdateViewBatch(aRequesterFuncName); // make sure selection is in view // After ScrollSelectionFocusIntoView(), the pending notifications might be @@ -1678,7 +1680,8 @@ nsresult EditorBase::CutAsAction(nsIPrincipal* aPrincipal) { // XXX This transaction name is referred by PlaceholderTransaction::Merge() // so that we need to keep using it here. AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::DeleteTxnName, - ScrollSelectionIntoView::Yes); + ScrollSelectionIntoView::Yes, + __FUNCTION__); rv = DeleteSelectionAsSubAction( eNone, IsTextEditor() ? nsIEditor::eNoStrip : nsIEditor::eStrip); NS_WARNING_ASSERTION( @@ -2610,8 +2613,8 @@ NS_IMETHODIMP EditorBase::CloneAttributes(Element* aDestElement, void EditorBase::CloneAttributesWithTransaction(Element& aDestElement, Element& aSourceElement) { - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); // Use transaction system for undo only if destination is already in the // document @@ -2980,11 +2983,11 @@ nsresult EditorBase::InsertTextIntoTextNodeWithTransaction( // XXX We may not need these view batches anymore. This is handled at a // higher level now I believe. - BeginUpdateViewBatch(); + BeginUpdateViewBatch(__FUNCTION__); nsresult rv = DoTransactionInternal(transaction); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::DoTransactionInternal() failed"); - EndUpdateViewBatch(); + EndUpdateViewBatch(__FUNCTION__); if (IsHTMLEditor() && pointToInsert.IsSet()) { auto [begin, end] = ComputeInsertedRange(pointToInsert, aStringToInsert); @@ -3408,23 +3411,23 @@ nsresult EditorBase::EnsurePaddingBRElementInMultilineEditor() { return NS_OK; } -void EditorBase::BeginUpdateViewBatch() { +void EditorBase::BeginUpdateViewBatch(const char* aRequesterFuncName) { MOZ_ASSERT(IsEditActionDataAvailable()); MOZ_ASSERT(mUpdateCount >= 0, "bad state"); if (!mUpdateCount) { // Turn off selection updates and notifications. - SelectionRef().StartBatchChanges(); + SelectionRef().StartBatchChanges(aRequesterFuncName); } mUpdateCount++; } -void EditorBase::EndUpdateViewBatch() { +void EditorBase::EndUpdateViewBatch(const char* aRequesterFuncName) { MOZ_ASSERT(IsEditActionDataAvailable()); MOZ_ASSERT(mUpdateCount > 0, "bad state"); - if (mUpdateCount <= 0) { + if (NS_WARN_IF(mUpdateCount <= 0)) { mUpdateCount = 0; return; } @@ -3434,7 +3437,7 @@ void EditorBase::EndUpdateViewBatch() { } // Turn selection updating and notifications back on. - SelectionRef().EndBatchChanges(); + SelectionRef().EndBatchChanges(aRequesterFuncName); } TextComposition* EditorBase::GetComposition() const { return mComposition; } @@ -3595,7 +3598,8 @@ nsresult EditorBase::OnCompositionChange( { AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::IMETxnName, - ScrollSelectionIntoView::Yes); + ScrollSelectionIntoView::Yes, + __FUNCTION__); MOZ_ASSERT( mIsInEditSubAction, @@ -4175,7 +4179,8 @@ nsresult EditorBase::DeleteSelectionAsAction( // delete placeholder txns merge. AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::DeleteTxnName, - ScrollSelectionIntoView::Yes); + ScrollSelectionIntoView::Yes, + __FUNCTION__); rv = DeleteSelectionAsSubAction(aDirectionAndAmount, aStripWrappers); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::DeleteSelectionAsSubAction() failed"); @@ -4407,11 +4412,11 @@ nsresult EditorBase::HandleDropEvent(DragEvent* aDropEvent) { } // Combine any deletion and drop insertion into one transaction. - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); // Don't dispatch "selectionchange" event until inserting all contents. - SelectionBatcher selectionBatcher(SelectionRef()); + SelectionBatcher selectionBatcher(SelectionRef(), __FUNCTION__); // Track dropped point with nsRange because we shouldn't insert the // dropped content into different position even if some event listeners @@ -4561,7 +4566,8 @@ nsresult EditorBase::DeleteSelectionByDragAsAction(bool aDispatchInputEvent) { // But keep using placeholder transaction for "insertFromDrop" if there is. Maybe treatAsOneTransaction; if (requestedByAnotherEditor) { - treatAsOneTransaction.emplace(*this, ScrollSelectionIntoView::Yes); + treatAsOneTransaction.emplace(*this, ScrollSelectionIntoView::Yes, + __FUNCTION__); } rv = DeleteSelectionAsSubAction(nsIEditor::eNone, IsTextEditor() @@ -4967,7 +4973,8 @@ nsresult EditorBase::OnInputText(const nsAString& aStringToInsert) { } AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::TypingTxnName, - ScrollSelectionIntoView::Yes); + ScrollSelectionIntoView::Yes, + __FUNCTION__); rv = InsertTextAsSubAction(aStringToInsert, SelectionHandling::Delete); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::InsertTextAsSubAction() failed"); @@ -5028,8 +5035,8 @@ nsresult EditorBase::ReplaceTextAsAction( return EditorBase::ToGenericNSResult(rv); } - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); // This should emulates inserting text for better undo/redo behavior. IgnoredErrorResult ignoredError; @@ -5063,7 +5070,7 @@ nsresult EditorBase::ReplaceTextAsAction( // Note that do not notify selectionchange caused by selecting all text // because it's preparation of our delete implementation so web apps // shouldn't receive such selectionchange before the first mutation. - AutoUpdateViewBatch preventSelectionChangeEvent(*this); + AutoUpdateViewBatch preventSelectionChangeEvent(*this, __FUNCTION__); // Select the range but as far as possible, we should not create new range // even if it's part of special Selection. @@ -5928,8 +5935,8 @@ nsresult EditorBase::InsertTextAsAction(const nsAString& aStringToInsert, if (IsTextEditor()) { nsContentUtils::PlatformToDOMLineBreaks(stringToInsert); } - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); rv = InsertTextAsSubAction(stringToInsert, SelectionHandling::Delete); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::InsertTextAsSubAction() failed"); @@ -6064,6 +6071,63 @@ EditorBase::AutoEditActionDataSetter::~AutoEditActionDataSetter() { "mTopLevelEditSubActionData.mSelectedRange should've been cleared"); } +void EditorBase::AutoEditActionDataSetter::UpdateSelectionCache( + Selection& aSelection) { + MOZ_ASSERT(aSelection.GetType() == SelectionType::eNormal); + + if (mSelection == &aSelection) { + return; + } + + AutoEditActionDataSetter& topLevelEditActionData = + [&]() -> AutoEditActionDataSetter& { + for (AutoEditActionDataSetter* editActionData = this;; + editActionData = editActionData->mParentData) { + if (!editActionData->mParentData) { + return *editActionData; + } + } + MOZ_ASSERT_UNREACHABLE("You do something wrong"); + }(); + + // Keep grabbing the old selection in the top level edit action data until the + // all owners end handling it. + if (mSelection) { + topLevelEditActionData.mRetiredSelections.AppendElement(*mSelection); + } + + // If the old selection is in batch, we should end the batch which + // `EditorBase::BeginUpdateViewBatch` started. + if (mEditorBase.mUpdateCount && mSelection) { + mSelection->EndBatchChanges(__FUNCTION__); + } + + Selection* previousSelection = mSelection; + mSelection = &aSelection; + for (AutoEditActionDataSetter* parentActionData = mParentData; + parentActionData; parentActionData = parentActionData->mParentData) { + if (!parentActionData->mSelection) { + continue; + } + // Skip scanning mRetiredSelections if we've already handled the selection + // previous time. + if (parentActionData->mSelection != previousSelection) { + if (!topLevelEditActionData.mRetiredSelections.Contains( + OwningNonNull(*parentActionData->mSelection))) { + topLevelEditActionData.mRetiredSelections.AppendElement( + *parentActionData->mSelection); + } + previousSelection = parentActionData->mSelection; + } + parentActionData->mSelection = &aSelection; + } + + // Restart the batching in the new selection. + if (mEditorBase.mUpdateCount) { + aSelection.StartBatchChanges(__FUNCTION__); + } +} + void EditorBase::AutoEditActionDataSetter::SetColorData( const nsAString& aData) { MOZ_ASSERT(!HasTriedToDispatchBeforeInputEvent(), diff --git a/editor/libeditor/EditorBase.h b/editor/libeditor/EditorBase.h index 24f6b93c255f..3cae3348e164 100644 --- a/editor/libeditor/EditorBase.h +++ b/editor/libeditor/EditorBase.h @@ -1303,17 +1303,7 @@ class EditorBase : public nsIEditor, return mParentData ? mParentData->RangeUpdaterRef() : mRangeUpdater; } - void UpdateSelectionCache(Selection& aSelection) { - MOZ_ASSERT(aSelection.GetType() == SelectionType::eNormal); - - AutoEditActionDataSetter* actionData = this; - while (actionData) { - if (actionData->mSelection) { - actionData->mSelection = &aSelection; - } - actionData = actionData->mParentData; - } - } + void UpdateSelectionCache(Selection& aSelection); private: bool IsBeforeInputEventEnabled() const; @@ -1377,6 +1367,7 @@ class EditorBase : public nsIEditor, EditorBase& mEditorBase; RefPtr mSelection; + nsTArray> mRetiredSelections; nsCOMPtr mPrincipal; // EditAction may be nested, for example, a command may be executed // from mutation event listener which is run while editor changes @@ -2125,13 +2116,14 @@ class EditorBase : public nsIEditor, * manager batches. */ MOZ_CAN_RUN_SCRIPT_BOUNDARY void BeginPlaceholderTransaction( - nsStaticAtom& aTransactionName); + nsStaticAtom& aTransactionName, const char* aRequesterFuncName); enum class ScrollSelectionIntoView { No, Yes }; MOZ_CAN_RUN_SCRIPT_BOUNDARY void EndPlaceholderTransaction( - ScrollSelectionIntoView aScrollSelectionIntoView); + ScrollSelectionIntoView aScrollSelectionIntoView, + const char* aRequesterFuncName); - void BeginUpdateViewBatch(); - MOZ_CAN_RUN_SCRIPT void EndUpdateViewBatch(); + void BeginUpdateViewBatch(const char* aRequesterFuncName); + MOZ_CAN_RUN_SCRIPT void EndUpdateViewBatch(const char* aRequesterFuncName); /** * Used by HTMLEditor::AutoTransactionBatch, nsIEditor::BeginTransaction @@ -2141,8 +2133,10 @@ class EditorBase : public nsIEditor, * XXX What's the difference with PlaceholderTransaction? Should we always * use it instead? */ - MOZ_CAN_RUN_SCRIPT void BeginTransactionInternal(); - MOZ_CAN_RUN_SCRIPT void EndTransactionInternal(); + MOZ_CAN_RUN_SCRIPT void BeginTransactionInternal( + const char* aRequesterFuncName); + MOZ_CAN_RUN_SCRIPT void EndTransactionInternal( + const char* aRequesterFuncName); protected: // Shouldn't be used by friend classes /** @@ -2584,28 +2578,41 @@ class EditorBase : public nsIEditor, */ class MOZ_RAII AutoPlaceholderBatch final { public: + /** + * @param aRequesterFuncName function name which wants to end the batch. + * This won't be stored nor exposed to selection listeners etc, used only + * for logging. This MUST be alive when the destructor runs. + */ AutoPlaceholderBatch(EditorBase& aEditorBase, - ScrollSelectionIntoView aScrollSelectionIntoView) + ScrollSelectionIntoView aScrollSelectionIntoView, + const char* aRequesterFuncName) : mEditorBase(aEditorBase), - mScrollSelectionIntoView(aScrollSelectionIntoView) { - mEditorBase->BeginPlaceholderTransaction(*nsGkAtoms::_empty); + mScrollSelectionIntoView(aScrollSelectionIntoView), + mRequesterFuncName(aRequesterFuncName) { + mEditorBase->BeginPlaceholderTransaction(*nsGkAtoms::_empty, + mRequesterFuncName); } AutoPlaceholderBatch(EditorBase& aEditorBase, nsStaticAtom& aTransactionName, - ScrollSelectionIntoView aScrollSelectionIntoView) + ScrollSelectionIntoView aScrollSelectionIntoView, + const char* aRequesterFuncName) : mEditorBase(aEditorBase), - mScrollSelectionIntoView(aScrollSelectionIntoView) { - mEditorBase->BeginPlaceholderTransaction(aTransactionName); + mScrollSelectionIntoView(aScrollSelectionIntoView), + mRequesterFuncName(aRequesterFuncName) { + mEditorBase->BeginPlaceholderTransaction(aTransactionName, + mRequesterFuncName); } ~AutoPlaceholderBatch() { - mEditorBase->EndPlaceholderTransaction(mScrollSelectionIntoView); + mEditorBase->EndPlaceholderTransaction(mScrollSelectionIntoView, + mRequesterFuncName); } protected: - OwningNonNull mEditorBase; - ScrollSelectionIntoView mScrollSelectionIntoView; + const OwningNonNull mEditorBase; + const ScrollSelectionIntoView mScrollSelectionIntoView; + const char* const mRequesterFuncName; }; /** @@ -2672,17 +2679,24 @@ class EditorBase : public nsIEditor, */ class MOZ_RAII AutoUpdateViewBatch final { public: - MOZ_CAN_RUN_SCRIPT explicit AutoUpdateViewBatch(EditorBase& aEditorBase) - : mEditorBase(aEditorBase) { - mEditorBase.BeginUpdateViewBatch(); + /** + * @param aRequesterFuncName function name which wants to end the batch. + * This won't be stored nor exposed to selection listeners etc, used only + * for logging. This MUST be alive when the destructor runs. + */ + MOZ_CAN_RUN_SCRIPT explicit AutoUpdateViewBatch( + EditorBase& aEditorBase, const char* aRequesterFuncName) + : mEditorBase(aEditorBase), mRequesterFuncName(aRequesterFuncName) { + mEditorBase.BeginUpdateViewBatch(mRequesterFuncName); } MOZ_CAN_RUN_SCRIPT ~AutoUpdateViewBatch() { - MOZ_KnownLive(mEditorBase).EndUpdateViewBatch(); + MOZ_KnownLive(mEditorBase).EndUpdateViewBatch(mRequesterFuncName); } protected: EditorBase& mEditorBase; + const char* const mRequesterFuncName; }; protected: diff --git a/editor/libeditor/HTMLAbsPositionEditor.cpp b/editor/libeditor/HTMLAbsPositionEditor.cpp index 1d0e65443ada..e1f5e5552d84 100644 --- a/editor/libeditor/HTMLAbsPositionEditor.cpp +++ b/editor/libeditor/HTMLAbsPositionEditor.cpp @@ -567,8 +567,8 @@ nsresult HTMLEditor::SetFinalPosition(int32_t aX, int32_t aY) { y.AppendInt(newY); // we want one transaction only from a user's point of view - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); if (NS_WARN_IF(!mAbsolutelyPositionedObject)) { return NS_ERROR_FAILURE; @@ -647,8 +647,8 @@ nsresult HTMLEditor::SetPositionToAbsoluteOrStatic(Element& aElement, nsresult HTMLEditor::SetPositionToAbsolute(Element& aElement) { MOZ_ASSERT(IsEditActionDataAvailable()); - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); int32_t x, y; DebugOnly rvIgnored = GetElementOrigin(aElement, x, y); @@ -708,8 +708,8 @@ nsresult HTMLEditor::SetPositionToStatic(Element& aElement) { return NS_ERROR_INVALID_ARG; } - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); nsresult rv; // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted @@ -854,8 +854,8 @@ NS_IMETHODIMP HTMLEditor::GetGridSize(uint32_t* aSize) { nsresult HTMLEditor::SetTopAndLeftWithTransaction( nsStyledElement& aStyledElement, int32_t aX, int32_t aY) { - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); nsresult rv; rv = mCSSEditUtils->SetCSSPropertyPixelsWithTransaction(aStyledElement, *nsGkAtoms::left, aX); diff --git a/editor/libeditor/HTMLEditSubActionHandler.cpp b/editor/libeditor/HTMLEditSubActionHandler.cpp index be34887609fc..26bce29ac0af 100644 --- a/editor/libeditor/HTMLEditSubActionHandler.cpp +++ b/editor/libeditor/HTMLEditSubActionHandler.cpp @@ -1392,7 +1392,8 @@ nsresult HTMLEditor::InsertLineBreakAsSubAction() { // XXX This may be called by execCommand() with "insertLineBreak". // In such case, naming the transaction "TypingTxnName" is odd. AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::TypingTxnName, - ScrollSelectionIntoView::Yes); + ScrollSelectionIntoView::Yes, + __FUNCTION__); // calling it text insertion to trigger moz br treatment by rules // XXX Why do we use EditSubAction::eInsertText here? Looks like @@ -1519,7 +1520,8 @@ EditActionResult HTMLEditor::InsertParagraphSeparatorAsSubAction() { // XXX This may be called by execCommand() with "insertParagraph". // In such case, naming the transaction "TypingTxnName" is odd. AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::TypingTxnName, - ScrollSelectionIntoView::Yes); + ScrollSelectionIntoView::Yes, + __FUNCTION__); IgnoredErrorResult ignoredError; AutoEditSubActionNotifier startToHandleEditSubAction( @@ -2856,8 +2858,8 @@ EditActionResult HTMLEditor::MakeOrChangeListAndListItemAsSubAction( return EditActionIgnored(); } - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); // XXX EditSubAction::eCreateOrChangeDefinitionListItem and // EditSubAction::eCreateOrChangeList are treated differently in @@ -3449,8 +3451,8 @@ nsresult HTMLEditor::RemoveListAtSelectionAsSubAction() { return result.Rv(); } - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); IgnoredErrorResult ignoredError; AutoEditSubActionNotifier startToHandleEditSubAction( *this, EditSubAction::eRemoveList, nsIEditor::eNext, ignoredError); @@ -3757,8 +3759,8 @@ nsresult HTMLEditor::MaybeInsertPaddingBRElementForEmptyLastLineAtSelection() { EditActionResult HTMLEditor::IndentAsSubAction() { MOZ_ASSERT(IsEditActionDataAvailable()); - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); IgnoredErrorResult ignoredError; AutoEditSubActionNotifier startToHandleEditSubAction( *this, EditSubAction::eIndent, nsIEditor::eNext, ignoredError); @@ -4425,8 +4427,8 @@ nsresult HTMLEditor::HandleHTMLIndentAtSelectionInternal() { EditActionResult HTMLEditor::OutdentAsSubAction() { MOZ_ASSERT(IsEditActionDataAvailable()); - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); IgnoredErrorResult ignoredError; AutoEditSubActionNotifier startToHandleEditSubAction( *this, EditSubAction::eOutdent, nsIEditor::eNext, ignoredError); @@ -5191,8 +5193,8 @@ nsresult HTMLEditor::CreateStyleForInsertText( EditActionResult HTMLEditor::AlignAsSubAction(const nsAString& aAlignType) { MOZ_ASSERT(IsEditActionDataAvailable()); - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); IgnoredErrorResult ignoredError; AutoEditSubActionNotifier startToHandleEditSubAction( *this, EditSubAction::eSetOrClearAlignment, nsIEditor::eNext, @@ -9515,8 +9517,8 @@ nsresult HTMLEditor::ChangeMarginStart(Element& aElement, EditActionResult HTMLEditor::SetSelectionToAbsoluteAsSubAction() { MOZ_ASSERT(IsTopLevelEditSubActionDataAvailable()); - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); IgnoredErrorResult ignoredError; AutoEditSubActionNotifier startToHandleEditSubAction( *this, EditSubAction::eSetPositionToAbsolute, nsIEditor::eNext, @@ -9920,8 +9922,8 @@ nsresult HTMLEditor::MoveSelectedContentsToDivElementToMakeItAbsolutePosition( EditActionResult HTMLEditor::SetSelectionToStaticAsSubAction() { MOZ_ASSERT(IsEditActionDataAvailable()); - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); IgnoredErrorResult ignoredError; AutoEditSubActionNotifier startToHandleEditSubAction( *this, EditSubAction::eSetPositionToStatic, nsIEditor::eNext, @@ -9999,8 +10001,8 @@ EditActionResult HTMLEditor::SetSelectionToStaticAsSubAction() { EditActionResult HTMLEditor::AddZIndexAsSubAction(int32_t aChange) { MOZ_ASSERT(IsEditActionDataAvailable()); - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); IgnoredErrorResult ignoredError; AutoEditSubActionNotifier startToHandleEditSubAction( *this, diff --git a/editor/libeditor/HTMLEditor.cpp b/editor/libeditor/HTMLEditor.cpp index 9c9b6422551d..f55302e31bbf 100644 --- a/editor/libeditor/HTMLEditor.cpp +++ b/editor/libeditor/HTMLEditor.cpp @@ -1399,8 +1399,8 @@ nsresult HTMLEditor::ReplaceHeadContentsWithSourceWithTransaction( // Mac linebreaks: Map any remaining CR to LF: inputString.ReplaceSubstring(u"\r"_ns, u"\n"_ns); - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); // Get the first range in the selection, for context: RefPtr range = SelectionRef().GetRangeAt(0); @@ -1504,8 +1504,8 @@ NS_IMETHODIMP HTMLEditor::RebuildDocumentFromSource( } // Time to change the document - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); nsReadingIterator endtotal; aSourceString.EndReading(endtotal); @@ -1728,8 +1728,8 @@ nsresult HTMLEditor::InsertElementAtSelectionAsAction( UndefineCaretBidiLevel(); - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); IgnoredErrorResult ignoredError; AutoEditSubActionNotifier startToHandleEditSubAction( *this, EditSubAction::eInsertElement, nsIEditor::eNext, ignoredError); @@ -2497,8 +2497,8 @@ nsresult HTMLEditor::FormatBlockContainerAsSubAction(nsAtom& aTagName) { MOZ_ASSERT(&aTagName != nsGkAtoms::dd && &aTagName != nsGkAtoms::dt); - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); IgnoredErrorResult ignoredError; AutoEditSubActionNotifier startToHandleEditSubAction( *this, EditSubAction::eCreateOrRemoveBlock, nsIEditor::eNext, @@ -3173,8 +3173,8 @@ nsresult HTMLEditor::InsertLinkAroundSelectionAsAction( return NS_OK; } - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); // Set all attributes found on the supplied anchor element RefPtr attributeMap = anchor->Attributes(); @@ -5364,8 +5364,8 @@ nsresult HTMLEditor::SetCSSBackgroundColorWithTransaction( bool selectionIsCollapsed = SelectionRef().IsCollapsed(); - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); IgnoredErrorResult ignoredError; AutoEditSubActionNotifier startToHandleEditSubAction( *this, EditSubAction::eInsertElement, nsIEditor::eNext, ignoredError); diff --git a/editor/libeditor/HTMLEditor.h b/editor/libeditor/HTMLEditor.h index 71e54c901638..7e9755e3e63f 100644 --- a/editor/libeditor/HTMLEditor.h +++ b/editor/libeditor/HTMLEditor.h @@ -4477,18 +4477,25 @@ class HTMLEditor final : public EditorBase, */ class MOZ_RAII AutoTransactionBatch final { public: - MOZ_CAN_RUN_SCRIPT explicit AutoTransactionBatch(HTMLEditor& aHTMLEditor) - : mHTMLEditor(aHTMLEditor) { - MOZ_KnownLive(mHTMLEditor).BeginTransactionInternal(); + /** + * @param aRequesterFuncName function name which wants to end the batch. + * This won't be stored nor exposed to selection listeners etc, used only + * for logging. This MUST be alive when the destructor runs. + */ + MOZ_CAN_RUN_SCRIPT explicit AutoTransactionBatch( + HTMLEditor& aHTMLEditor, const char* aRequesterFuncName) + : mHTMLEditor(aHTMLEditor), mRequesterFuncName(aRequesterFuncName) { + MOZ_KnownLive(mHTMLEditor).BeginTransactionInternal(mRequesterFuncName); } MOZ_CAN_RUN_SCRIPT ~AutoTransactionBatch() { - MOZ_KnownLive(mHTMLEditor).EndTransactionInternal(); + MOZ_KnownLive(mHTMLEditor).EndTransactionInternal(mRequesterFuncName); } protected: // The lifetime must be guaranteed by the creator of this instance. MOZ_KNOWN_LIVE HTMLEditor& mHTMLEditor; + const char* const mRequesterFuncName; }; RefPtr mTypeInState; diff --git a/editor/libeditor/HTMLEditorDataTransfer.cpp b/editor/libeditor/HTMLEditorDataTransfer.cpp index 3637618eaffb..abc42806e957 100644 --- a/editor/libeditor/HTMLEditorDataTransfer.cpp +++ b/editor/libeditor/HTMLEditorDataTransfer.cpp @@ -147,8 +147,8 @@ nsresult HTMLEditor::LoadHTML(const nsAString& aInputString) { return NS_ERROR_EDITOR_DESTROYED; } - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); IgnoredErrorResult ignoredError; AutoEditSubActionNotifier startToHandleEditSubAction( *this, EditSubAction::eInsertHTMLSource, nsIEditor::eNext, ignoredError); @@ -504,8 +504,8 @@ nsresult HTMLEditor::HTMLWithContextInserter::Run( // force IME commit; set up rules sniffing and batching mHTMLEditor.CommitComposition(); - AutoPlaceholderBatch treatAsOneTransaction(mHTMLEditor, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + mHTMLEditor, ScrollSelectionIntoView::Yes, __FUNCTION__); IgnoredErrorResult ignoredError; AutoEditSubActionNotifier startToHandleEditSubAction( MOZ_KnownLive(mHTMLEditor), EditSubAction::ePasteHTMLContent, @@ -1513,8 +1513,8 @@ nsresult HTMLEditor::BlobReader::OnResult(const nsACString& aResult) { return EditorBase::ToGenericNSResult(rv); } - AutoPlaceholderBatch treatAsOneTransaction(*mHTMLEditor, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *mHTMLEditor, ScrollSelectionIntoView::Yes, __FUNCTION__); EditorDOMPoint pointToInsert(mPointToInsert); rv = MOZ_KnownLive(mHTMLEditor) ->DoInsertHTMLWithContext(stuffToPaste, u""_ns, u""_ns, @@ -1730,8 +1730,8 @@ nsresult HTMLEditor::InsertObject(const nsACString& aType, nsISupports* aObject, return rv; } - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); rv = DoInsertHTMLWithContext( stuffToPaste, u""_ns, u""_ns, NS_LITERAL_STRING_FROM_CSTRING(kFileMime), aPointToInsert, aDoDeleteSelection, aIsSafe, false); @@ -1806,7 +1806,7 @@ nsresult HTMLEditor::InsertFromTransferable(nsITransferable* aTransferable, getter_Copies(cfcontext)); if (NS_SUCCEEDED(rv) && !cffragment.IsEmpty()) { AutoPlaceholderBatch treatAsOneTransaction( - *this, ScrollSelectionIntoView::Yes); + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); // If we have our private HTML flavor, we will only use the fragment // from the CF_HTML. The rest comes from the clipboard. if (aHavePrivateHTMLFlavor) { @@ -1850,7 +1850,7 @@ nsresult HTMLEditor::InsertFromTransferable(nsITransferable* aTransferable, if (!stuffToPaste.IsEmpty()) { AutoPlaceholderBatch treatAsOneTransaction( - *this, ScrollSelectionIntoView::Yes); + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); if (bestFlavor.EqualsLiteral(kHTMLMime)) { nsresult rv = DoInsertHTMLWithContext( stuffToPaste, aContextStr, aInfoStr, flavor, EditorDOMPoint(), @@ -2208,8 +2208,8 @@ nsresult HTMLEditor::PasteTransferableAsAction(nsITransferable* aTransferable, RefPtr dataTransfer = GetInputEventDataTransfer(); if (dataTransfer->HasFile() && dataTransfer->MozItemCount() > 0) { // Now aTransferable has moved to DataTransfer. Use DataTransfer. - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); rv = InsertFromDataTransfer(dataTransfer, 0, nullptr, EditorDOMPoint(), true); @@ -2437,8 +2437,8 @@ nsresult HTMLEditor::PasteAsQuotationAsAction(int32_t aClipboardType, UndefineCaretBidiLevel(); - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); IgnoredErrorResult ignoredError; AutoEditSubActionNotifier startToHandleEditSubAction( *this, EditSubAction::eInsertQuotation, nsIEditor::eNext, ignoredError); @@ -2567,8 +2567,8 @@ nsresult HTMLEditor::PasteAsPlaintextQuotation(int32_t aSelectionType) { return NS_OK; } - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); rv = InsertAsPlaintextQuotation(stuffToPaste, true, 0); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::InsertAsPlaintextQuotation() failed"); @@ -2663,9 +2663,9 @@ nsresult HTMLEditor::InsertTextWithQuotations( // The whole operation should be undoable in one transaction: // XXX Why isn't enough to use only AutoPlaceholderBatch here? - AutoTransactionBatch bundleAllTransactions(*this); - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoTransactionBatch bundleAllTransactions(*this, __FUNCTION__); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); rv = InsertTextWithQuotationsInternal(aStringToInsert); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), @@ -2785,8 +2785,8 @@ nsresult HTMLEditor::InsertAsQuotation(const nsAString& aQuotedText, "CanHandleAndMaybeDispatchBeforeInputEvent(), failed"); return EditorBase::ToGenericNSResult(rv); } - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); rv = InsertAsPlaintextQuotation(aQuotedText, true, aNodeInserted); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::InsertAsPlaintextQuotation() failed"); @@ -2802,8 +2802,8 @@ nsresult HTMLEditor::InsertAsQuotation(const nsAString& aQuotedText, return EditorBase::ToGenericNSResult(rv); } - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); nsAutoString citation; rv = InsertAsCitedQuotationInternal(aQuotedText, citation, false, aNodeInserted); @@ -3019,9 +3019,9 @@ NS_IMETHODIMP HTMLEditor::Rewrap(bool aRespectNewlines) { // The whole operation in InsertTextWithQuotationsInternal() should be // undoable in one transaction. // XXX Why isn't enough to use only AutoPlaceholderBatch here? - AutoTransactionBatch bundleAllTransactions(*this); - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoTransactionBatch bundleAllTransactions(*this, __FUNCTION__); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); rv = InsertTextWithQuotationsInternal(wrapped); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::InsertTextWithQuotationsInternal() failed"); @@ -3049,8 +3049,8 @@ NS_IMETHODIMP HTMLEditor::InsertAsCitedQuotation(const nsAString& aQuotedText, return EditorBase::ToGenericNSResult(rv); } - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); rv = InsertAsPlaintextQuotation(aQuotedText, true, aNodeInserted); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::InsertAsPlaintextQuotation() failed"); @@ -3066,8 +3066,8 @@ NS_IMETHODIMP HTMLEditor::InsertAsCitedQuotation(const nsAString& aQuotedText, return EditorBase::ToGenericNSResult(rv); } - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); rv = InsertAsCitedQuotationInternal(aQuotedText, aCitation, aInsertHTML, aNodeInserted); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), diff --git a/editor/libeditor/HTMLEditorObjectResizer.cpp b/editor/libeditor/HTMLEditorObjectResizer.cpp index 0fb64a08be6d..a5860e9d2637 100644 --- a/editor/libeditor/HTMLEditorObjectResizer.cpp +++ b/editor/libeditor/HTMLEditorObjectResizer.cpp @@ -1260,8 +1260,8 @@ nsresult HTMLEditor::SetFinalSizeWithTransaction(int32_t aX, int32_t aY) { : 0); // we want one transaction only from a user's point of view - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); RefPtr resizedElement(mResizedObject); RefPtr resizedStyleElement = nsStyledElement::FromNodeOrNull(mResizedObject); diff --git a/editor/libeditor/HTMLStyleEditor.cpp b/editor/libeditor/HTMLStyleEditor.cpp index 251915cdfc2c..4e828c39ab8d 100644 --- a/editor/libeditor/HTMLStyleEditor.cpp +++ b/editor/libeditor/HTMLStyleEditor.cpp @@ -85,8 +85,8 @@ nsresult HTMLEditor::SetInlinePropertyAsAction(nsAtom& aProperty, // XXX Due to bug 1659276 and bug 1659924, we should not scroll selection // into view after setting the new style. - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::No); + AutoPlaceholderBatch treatAsOneTransaction(*this, ScrollSelectionIntoView::No, + __FUNCTION__); nsAtom* property = &aProperty; nsAtom* attribute = aAttribute; @@ -229,8 +229,8 @@ nsresult HTMLEditor::SetInlinePropertyInternal( return result.Rv(); } - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); IgnoredErrorResult ignoredError; AutoEditSubActionNotifier startToHandleEditSubAction( *this, EditSubAction::eInsertElement, nsIEditor::eNext, ignoredError); @@ -1724,8 +1724,8 @@ nsresult HTMLEditor::RemoveAllInlinePropertiesAsAction( return EditorBase::ToGenericNSResult(rv); } - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); IgnoredErrorResult ignoredError; AutoEditSubActionNotifier startToHandleEditSubAction( *this, EditSubAction::eRemoveAllTextProperties, nsIEditor::eNext, @@ -1898,8 +1898,8 @@ nsresult HTMLEditor::RemoveInlinePropertyInternal( return result.Rv(); } - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); IgnoredErrorResult ignoredError; AutoEditSubActionNotifier startToHandleEditSubAction( *this, EditSubAction::eRemoveTextProperty, nsIEditor::eNext, @@ -2260,8 +2260,8 @@ nsresult HTMLEditor::RelativeFontChange(FontSize aDir) { } // Wrap with txn batching, rules sniffing, and selection preservation code - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); IgnoredErrorResult ignoredError; AutoEditSubActionNotifier startToHandleEditSubAction( *this, EditSubAction::eSetTextProperty, nsIEditor::eNext, ignoredError); diff --git a/editor/libeditor/HTMLTableEditor.cpp b/editor/libeditor/HTMLTableEditor.cpp index 6ba8be812320..8341124ca2b9 100644 --- a/editor/libeditor/HTMLTableEditor.cpp +++ b/editor/libeditor/HTMLTableEditor.cpp @@ -237,8 +237,8 @@ nsresult HTMLEditor::InsertTableCellsWithTransaction( MOZ_ASSERT_UNREACHABLE("Invalid InsertPosition"); } - AutoPlaceholderBatch treateAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treateAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); // Prevent auto insertion of BR in new cell until we're done AutoEditSubActionNotifier startToHandleEditSubAction( *this, EditSubAction::eInsertNode, nsIEditor::eNext, ignoredError); @@ -485,8 +485,8 @@ nsresult HTMLEditor::InsertTableColumnsWithTransaction( // Should not be empty since we've already found a cell. MOZ_ASSERT(!tableSize.IsEmpty()); - AutoPlaceholderBatch treateAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treateAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); // Prevent auto insertion of
element in new cell until we're done. AutoEditSubActionNotifier startToHandleEditSubAction( *this, EditSubAction::eInsertNode, nsIEditor::eNext, ignoredError); @@ -718,8 +718,8 @@ nsresult HTMLEditor::InsertTableRowsWithTransaction( // Should not be empty since we've already found a cell. MOZ_ASSERT(!tableSize.IsEmpty()); - AutoPlaceholderBatch treateAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treateAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); // Prevent auto insertion of BR in new cell until we're done AutoEditSubActionNotifier startToHandleEditSubAction( *this, EditSubAction::eInsertNode, nsIEditor::eNext, ignoredError); @@ -973,8 +973,8 @@ NS_IMETHODIMP HTMLEditor::DeleteTable() { return NS_ERROR_FAILURE; } - AutoPlaceholderBatch treateAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treateAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); rv = DeleteTableElementAndChildrenWithTransaction(*table); NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), @@ -1024,8 +1024,8 @@ nsresult HTMLEditor::DeleteTableCellWithTransaction( return NS_ERROR_FAILURE; // XXX Should we just return NS_OK? } - AutoPlaceholderBatch treateAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treateAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); // Prevent rules testing until we're done IgnoredErrorResult ignoredError; AutoEditSubActionNotifier startToHandleEditSubAction( @@ -1317,8 +1317,8 @@ nsresult HTMLEditor::DeleteTableCellContentsWithTransaction() { return NS_ERROR_FAILURE; // XXX Should we just return NS_OK? } - AutoPlaceholderBatch treateAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treateAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); // Prevent rules testing until we're done IgnoredErrorResult ignoredError; AutoEditSubActionNotifier startToHandleEditSubAction( @@ -1415,8 +1415,8 @@ nsresult HTMLEditor::DeleteSelectedTableColumnsWithTransaction( return error.StealNSResult(); } - AutoPlaceholderBatch treateAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treateAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); // Prevent rules testing until we're done IgnoredErrorResult ignoredError; @@ -1671,8 +1671,8 @@ nsresult HTMLEditor::DeleteSelectedTableRowsWithTransaction( return error.StealNSResult(); } - AutoPlaceholderBatch treateAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treateAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); // Prevent rules testing until we're done IgnoredErrorResult ignoredError; @@ -2007,7 +2007,7 @@ NS_IMETHODIMP HTMLEditor::SelectAllTableCells() { // Suppress nsISelectionListener notification // until all selection changes are finished - SelectionBatcher selectionBatcher(SelectionRef()); + SelectionBatcher selectionBatcher(SelectionRef(), __FUNCTION__); // It is now safe to clear the selection // BE SURE TO RESET IT BEFORE LEAVING! @@ -2122,7 +2122,7 @@ NS_IMETHODIMP HTMLEditor::SelectTableRow() { // Suppress nsISelectionListener notification // until all selection changes are finished - SelectionBatcher selectionBatcher(SelectionRef()); + SelectionBatcher selectionBatcher(SelectionRef(), __FUNCTION__); // It is now safe to clear the selection // BE SURE TO RESET IT BEFORE LEAVING! @@ -2231,7 +2231,7 @@ NS_IMETHODIMP HTMLEditor::SelectTableColumn() { // Suppress nsISelectionListener notification // until all selection changes are finished - SelectionBatcher selectionBatcher(SelectionRef()); + SelectionBatcher selectionBatcher(SelectionRef(), __FUNCTION__); // It is now safe to clear the selection // BE SURE TO RESET IT BEFORE LEAVING! @@ -2334,8 +2334,8 @@ NS_IMETHODIMP HTMLEditor::SplitTableCell() { return NS_OK; } - AutoPlaceholderBatch treateAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treateAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); // Prevent auto insertion of BR in new cell until we're done IgnoredErrorResult ignoredError; AutoEditSubActionNotifier startToHandleEditSubAction( @@ -2609,8 +2609,8 @@ NS_IMETHODIMP HTMLEditor::SwitchTableCellHeaderType(Element* aSourceCell, return EditorBase::ToGenericNSResult(rv); } - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); // Prevent auto insertion of BR in new cell created by // ReplaceContainerAndCloneAttributesWithTransaction(). IgnoredErrorResult ignoredError; @@ -2682,8 +2682,8 @@ NS_IMETHODIMP HTMLEditor::JoinTableCells(bool aMergeNonContiguousContents) { return NS_ERROR_FAILURE; // XXX Should we just return NS_OK? } - AutoPlaceholderBatch treateAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treateAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); // Don't let Rules System change the selection AutoTransactionsConserveSelection dontChangeSelection(*this); @@ -3347,8 +3347,8 @@ nsresult HTMLEditor::NormalizeTableInternal(Element& aTableOrElementInTable) { // Save current selection AutoSelectionRestorer restoreSelectionLater(*this); - AutoPlaceholderBatch treateAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treateAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); // Prevent auto insertion of BR in new cell until we're done IgnoredErrorResult ignoredError; AutoEditSubActionNotifier startToHandleEditSubAction( diff --git a/editor/libeditor/TextEditSubActionHandler.cpp b/editor/libeditor/TextEditSubActionHandler.cpp index 7c5b845c8e6e..4d2d5bb7c250 100644 --- a/editor/libeditor/TextEditSubActionHandler.cpp +++ b/editor/libeditor/TextEditSubActionHandler.cpp @@ -631,7 +631,7 @@ EditActionResult TextEditor::HandleDeleteSelectionInternal( // want to send a single selectionchange event to the document, so we // batch the selectionchange events, such that a single event fires after // the AutoHideSelectionChanges destructor has been run. - SelectionBatcher selectionBatcher(SelectionRef()); + SelectionBatcher selectionBatcher(SelectionRef(), __FUNCTION__); AutoHideSelectionChanges hideSelection(SelectionRef()); nsAutoScriptBlocker scriptBlocker; diff --git a/editor/libeditor/TextEditor.cpp b/editor/libeditor/TextEditor.cpp index 77c18d1a24fc..7b045ec608e2 100644 --- a/editor/libeditor/TextEditor.cpp +++ b/editor/libeditor/TextEditor.cpp @@ -282,8 +282,8 @@ NS_IMETHODIMP TextEditor::InsertLineBreak() { return NS_ERROR_FAILURE; } - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); rv = InsertLineBreakAsSubAction(); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "TextEditor::InsertLineBreakAsSubAction() failed"); @@ -307,7 +307,8 @@ nsresult TextEditor::InsertLineBreakAsAction(nsIPrincipal* aPrincipal) { // XXX This may be called by execCommand() with "insertParagraph". // In such case, naming the transaction "TypingTxnName" is odd. AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::TypingTxnName, - ScrollSelectionIntoView::Yes); + ScrollSelectionIntoView::Yes, + __FUNCTION__); rv = InsertLineBreakAsSubAction(); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::InsertLineBreakAsSubAction() failed"); @@ -332,8 +333,8 @@ nsresult TextEditor::SetTextAsAction( return EditorBase::ToGenericNSResult(rv); } - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); rv = SetTextAsSubAction(aString); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "TextEditor::SetTextAsSubAction() failed"); @@ -372,7 +373,7 @@ nsresult TextEditor::SetTextAsSubAction(const nsAString& aString) { // Note that do not notify selectionchange caused by selecting all text // because it's preparation of our delete implementation so web apps // shouldn't receive such selectionchange before the first mutation. - AutoUpdateViewBatch preventSelectionChangeEvent(*this); + AutoUpdateViewBatch preventSelectionChangeEvent(*this, __FUNCTION__); // XXX We should make ReplaceSelectionAsSubAction() take range. Then, // we can saving the expensive cost of modifying `Selection` here. @@ -561,8 +562,8 @@ nsresult TextEditor::PasteAsQuotationAsAction(int32_t aClipboardType, return EditorBase::ToGenericNSResult(rv); } - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); rv = InsertWithQuotationsAsSubAction(stuffToPaste); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "TextEditor::InsertWithQuotationsAsSubAction() failed"); diff --git a/editor/libeditor/TextEditorDataTransfer.cpp b/editor/libeditor/TextEditorDataTransfer.cpp index f0f259d537b6..f3a7e247d8df 100644 --- a/editor/libeditor/TextEditorDataTransfer.cpp +++ b/editor/libeditor/TextEditorDataTransfer.cpp @@ -76,8 +76,8 @@ nsresult TextEditor::InsertTextFromTransferable( // Sanitize possible carriage returns in the string to be inserted nsContentUtils::PlatformToDOMLineBreaks(stuffToPaste); - AutoPlaceholderBatch treatAsOneTransaction(*this, - ScrollSelectionIntoView::Yes); + AutoPlaceholderBatch treatAsOneTransaction( + *this, ScrollSelectionIntoView::Yes, __FUNCTION__); nsresult rv = InsertTextAsSubAction(stuffToPaste, SelectionHandling::Delete); if (NS_FAILED(rv)) { diff --git a/gfx/config/gfxFeature.h b/gfx/config/gfxFeature.h index d32daad4d984..3811ce2a1199 100644 --- a/gfx/config/gfxFeature.h +++ b/gfx/config/gfxFeature.h @@ -40,6 +40,7 @@ namespace gfx { _(DMABUF, Feature, "DMABUF") \ _(WINDOW_OCCLUSION, Feature, "WINDOW_OCCLUSION") \ _(VAAPI, Feature, "VA-API video decoding") \ + _(VIDEO_OVERLAY, Feature, "video overlay") \ /* Add new entries above this comment */ enum class Feature : uint32_t { diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index 9b982f1c42ea..249acb401139 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -2654,13 +2654,22 @@ void gfxPlatform::InitWebRenderConfig() { if (StaticPrefs::gfx_webrender_software_d3d11_AtStartup()) { gfxVars::SetAllowSoftwareWebRenderD3D11(true); } + + bool useVideoOverlay = false; if (StaticPrefs::gfx_webrender_dcomp_video_overlay_win_AtStartup()) { if (IsWin10AnniversaryUpdateOrLater() && gfxConfig::IsEnabled(Feature::WEBRENDER_COMPOSITOR)) { MOZ_ASSERT(gfxConfig::IsEnabled(Feature::WEBRENDER_DCOMP_PRESENT)); - gfxVars::SetUseWebRenderDCompVideoOverlayWin(true); + useVideoOverlay = true; } } + + if (useVideoOverlay) { + FeatureState& feature = gfxConfig::GetFeature(Feature::VIDEO_OVERLAY); + feature.EnableByDefault(); + gfxVars::SetUseWebRenderDCompVideoOverlayWin(true); + } + if (Preferences::GetBool("gfx.webrender.flip-sequential", false)) { if (UseWebRender() && gfxVars::UseWebRenderANGLE()) { gfxVars::SetUseWebRenderFlipSequentialWin(true); diff --git a/js/public/Class.h b/js/public/Class.h index 77bf472a8851..cb37b8188e3d 100644 --- a/js/public/Class.h +++ b/js/public/Class.h @@ -141,10 +141,14 @@ class ObjectOpResult { JS_PUBLIC_API bool failNoNamedSetter(); JS_PUBLIC_API bool failNoIndexedSetter(); JS_PUBLIC_API bool failNotDataDescriptor(); + JS_PUBLIC_API bool failInvalidDescriptor(); // Careful: This case has special handling in Object.defineProperty. JS_PUBLIC_API bool failCantDefineWindowNonConfigurable(); + JS_PUBLIC_API bool failBadArrayLength(); + JS_PUBLIC_API bool failBadIndex(); + uint32_t failureCode() const { MOZ_ASSERT(!ok()); return uint32_t(code_); diff --git a/js/src/devtools/automation/variants/asan b/js/src/devtools/automation/variants/asan index f31c88b9a06a..62091639f65b 100644 --- a/js/src/devtools/automation/variants/asan +++ b/js/src/devtools/automation/variants/asan @@ -4,7 +4,7 @@ "debug": false, "compiler": "clang", "env": { - "LLVM_SYMBOLIZER": "{MOZ_FETCHES_DIR}/llvm-symbolizer/llvm-symbolizer" + "LLVM_SYMBOLIZER": "{MOZ_FETCHES_DIR}/llvm-symbolizer/bin/llvm-symbolizer" }, "use_minidump": false } diff --git a/js/src/devtools/automation/variants/fuzzing b/js/src/devtools/automation/variants/fuzzing index 7e4db4b5a202..146b89e3554a 100644 --- a/js/src/devtools/automation/variants/fuzzing +++ b/js/src/devtools/automation/variants/fuzzing @@ -6,8 +6,8 @@ "env": { "JITTEST_EXTRA_ARGS": "--jitflags=none", "JSTESTS_EXTRA_ARGS": "--jitflags=none", - "LLVM_SYMBOLIZER": "{MOZ_FETCHES_DIR}/llvm-symbolizer/llvm-symbolizer", - "ASAN_SYMBOLIZER_PATH": "{MOZ_FETCHES_DIR}/llvm-symbolizer/llvm-symbolizer" + "LLVM_SYMBOLIZER": "{MOZ_FETCHES_DIR}/llvm-symbolizer/bin/llvm-symbolizer", + "ASAN_SYMBOLIZER_PATH": "{MOZ_FETCHES_DIR}/llvm-symbolizer/bin/llvm-symbolizer" }, "use_minidump": false } diff --git a/js/src/devtools/automation/variants/tsan b/js/src/devtools/automation/variants/tsan index d07b1d64aa18..69b2d87ef927 100644 --- a/js/src/devtools/automation/variants/tsan +++ b/js/src/devtools/automation/variants/tsan @@ -4,7 +4,7 @@ "debug": false, "compiler": "clang", "env": { - "LLVM_SYMBOLIZER": "{MOZ_FETCHES_DIR}/llvm-symbolizer/llvm-symbolizer", + "LLVM_SYMBOLIZER": "{MOZ_FETCHES_DIR}/llvm-symbolizer/bin/llvm-symbolizer", "JITTEST_EXTRA_ARGS": "--jitflags=tsan --ignore-timeouts={DIR}/cgc-jittest-timeouts.txt --unusable-error-status --exclude-from={DIR}/tsan-slow.txt", "JSTESTS_EXTRA_ARGS": "--exclude-file={DIR}/cgc-jstests-slow.txt" }, diff --git a/js/src/jit-test/tests/wasm/simd/avx2-x64-ion-codegen.js b/js/src/jit-test/tests/wasm/simd/avx2-x64-ion-codegen.js index 1cdedcb0a6aa..49244013178e 100644 --- a/js/src/jit-test/tests/wasm/simd/avx2-x64-ion-codegen.js +++ b/js/src/jit-test/tests/wasm/simd/avx2-x64-ion-codegen.js @@ -63,15 +63,56 @@ function codegenTestX64_T_v128_avxhack(inputs, options = {}) { } } +// Machers for any 64- and 32-bit registers. +var GPR_I64 = "%r\\w+"; +var GPR_I32 = "%(?:e\\w+|r\\d+d)"; + // Simple binary ops: e.g. add, sub, mul codegenTestX64_v128xv128_v128_avxhack( - [['i32x4.add', `c5 f1 fe c2 vpaddd %xmm2, %xmm1, %xmm0`], - ['i32x4.sub', `c5 f1 fa c2 vpsubd %xmm2, %xmm1, %xmm0`], - ['i32x4.mul', `c4 e2 71 40 c2 vpmulld %xmm2, %xmm1, %xmm0`], - ['f32x4.add', `c5 f0 58 c2 vaddps %xmm2, %xmm1, %xmm0`], - ['f32x4.sub', `c5 f0 5c c2 vsubps %xmm2, %xmm1, %xmm0`], - ['f32x4.mul', `c5 f0 59 c2 vmulps %xmm2, %xmm1, %xmm0`], - ['f32x4.div', `c5 f0 5e c2 vdivps %xmm2, %xmm1, %xmm0`]]); + [['i8x16.avgr_u', `c5 f1 e0 c2 vpavgb %xmm2, %xmm1, %xmm0`], + ['i16x8.avgr_u', `c5 f1 e3 c2 vpavgw %xmm2, %xmm1, %xmm0`], + ['i8x16.add', `c5 f1 fc c2 vpaddb %xmm2, %xmm1, %xmm0`], + ['i8x16.add_sat_s', `c5 f1 ec c2 vpaddsb %xmm2, %xmm1, %xmm0`], + ['i8x16.add_sat_u', `c5 f1 dc c2 vpaddusb %xmm2, %xmm1, %xmm0`], + ['i8x16.sub', `c5 f1 f8 c2 vpsubb %xmm2, %xmm1, %xmm0`], + ['i8x16.sub_sat_s', `c5 f1 e8 c2 vpsubsb %xmm2, %xmm1, %xmm0`], + ['i8x16.sub_sat_u', `c5 f1 d8 c2 vpsubusb %xmm2, %xmm1, %xmm0`], + ['i16x8.mul', `c5 f1 d5 c2 vpmullw %xmm2, %xmm1, %xmm0`], + ['i16x8.min_s', `c5 f1 ea c2 vpminsw %xmm2, %xmm1, %xmm0`], + ['i16x8.min_u', `c4 e2 71 3a c2 vpminuw %xmm2, %xmm1, %xmm0`], + ['i16x8.max_s', `c5 f1 ee c2 vpmaxsw %xmm2, %xmm1, %xmm0`], + ['i16x8.max_u', `c4 e2 71 3e c2 vpmaxuw %xmm2, %xmm1, %xmm0`], + ['i32x4.add', `c5 f1 fe c2 vpaddd %xmm2, %xmm1, %xmm0`], + ['i32x4.sub', `c5 f1 fa c2 vpsubd %xmm2, %xmm1, %xmm0`], + ['i32x4.mul', `c4 e2 71 40 c2 vpmulld %xmm2, %xmm1, %xmm0`], + ['i32x4.min_s', `c4 e2 71 39 c2 vpminsd %xmm2, %xmm1, %xmm0`], + ['i32x4.min_u', `c4 e2 71 3b c2 vpminud %xmm2, %xmm1, %xmm0`], + ['i32x4.max_s', `c4 e2 71 3d c2 vpmaxsd %xmm2, %xmm1, %xmm0`], + ['i32x4.max_u', `c4 e2 71 3f c2 vpmaxud %xmm2, %xmm1, %xmm0`], + ['i64x2.add', `c5 f1 d4 c2 vpaddq %xmm2, %xmm1, %xmm0`], + ['i64x2.sub', `c5 f1 fb c2 vpsubq %xmm2, %xmm1, %xmm0`], + ['i64x2.mul', ` +c5 e1 73 d1 20 vpsrlq \\$0x20, %xmm1, %xmm3 +66 0f f4 da pmuludq %xmm2, %xmm3 +c5 81 73 d2 20 vpsrlq \\$0x20, %xmm2, %xmm15 +66 44 0f f4 f9 pmuludq %xmm1, %xmm15 +66 44 0f d4 fb paddq %xmm3, %xmm15 +66 41 0f 73 f7 20 psllq \\$0x20, %xmm15 +c5 f1 f4 c2 vpmuludq %xmm2, %xmm1, %xmm0 +66 41 0f d4 c7 paddq %xmm15, %xmm0`], + ['f32x4.add', `c5 f0 58 c2 vaddps %xmm2, %xmm1, %xmm0`], + ['f32x4.sub', `c5 f0 5c c2 vsubps %xmm2, %xmm1, %xmm0`], + ['f32x4.mul', `c5 f0 59 c2 vmulps %xmm2, %xmm1, %xmm0`], + ['f32x4.div', `c5 f0 5e c2 vdivps %xmm2, %xmm1, %xmm0`], + ['f64x2.add', `c5 f1 58 c2 vaddpd %xmm2, %xmm1, %xmm0`], + ['f64x2.sub', `c5 f1 5c c2 vsubpd %xmm2, %xmm1, %xmm0`], + ['f64x2.mul', `c5 f1 59 c2 vmulpd %xmm2, %xmm1, %xmm0`], + ['f64x2.div', `c5 f1 5e c2 vdivpd %xmm2, %xmm1, %xmm0`], + ['i8x16.narrow_i16x8_s', `c5 f1 63 c2 vpacksswb %xmm2, %xmm1, %xmm0`], + ['i8x16.narrow_i16x8_u', `c5 f1 67 c2 vpackuswb %xmm2, %xmm1, %xmm0`], + ['i16x8.narrow_i32x4_s', `c5 f1 6b c2 vpackssdw %xmm2, %xmm1, %xmm0`], + ['i16x8.narrow_i32x4_u', `c4 e2 71 2b c2 vpackusdw %xmm2, %xmm1, %xmm0`], + ['i32x4.dot_i16x8_s', `c5 f1 f5 c2 vpmaddwd %xmm2, %xmm1, %xmm0`]]); // Simple comparison ops codegenTestX64_v128xv128_v128_avxhack( @@ -102,27 +143,40 @@ codegenTestX64_v128xv128_v128_avxhack( ['v128.xor', `c5 f1 ef c2 vpxor %xmm2, %xmm1, %xmm0`]]); +// Replace lane ops. +codegenTestX64_adhoc(`(module + (func (export "f") (param v128 v128 i32) (result v128) + (i8x16.replace_lane 7 (local.get 1) (local.get 2))))`, 'f', ` +c4 .. 71 20 .. 07 vpinsrb \\$0x07, ${GPR_I32}, %xmm1, %xmm0`); +codegenTestX64_adhoc(`(module + (func (export "f") (param v128 v128 i32) (result v128) + (i16x8.replace_lane 3 (local.get 1) (local.get 2))))`, 'f', ` +(?:c4 .. 71|c5 f1) c4 .. 03 vpinsrw \\$0x03, ${GPR_I32}, %xmm1, %xmm0`); +codegenTestX64_adhoc(`(module + (func (export "f") (param v128 v128 i32) (result v128) + (i32x4.replace_lane 2 (local.get 1) (local.get 2))))`, 'f', ` +c4 .. 71 22 .. 02 vpinsrd \\$0x02, ${GPR_I32}, %xmm1, %xmm0`); codegenTestX64_adhoc(`(module (func (export "f") (param v128 v128 i64) (result v128) - (i64x2.replace_lane 1 (local.get 1) (local.get 2))))`, - 'f', - ` -c4 .. f1 22 .. 01 vpinsrq \\$0x01, %r\\w+, %xmm1, %xmm0` ); // rdi (Linux) or r8 (Win) + (i64x2.replace_lane 1 (local.get 1) (local.get 2))))`, 'f', ` +c4 .. f1 22 .. 01 vpinsrq \\$0x01, ${GPR_I64}, %xmm1, %xmm0`); if (isAvxPresent(2)) { - // First i32 arg is: edi on Linux, and ecx on Windows. codegenTestX64_T_v128_avxhack( [['i32', 'i8x16.splat', ` -c5 f9 6e .. vmovd %e\\w+, %xmm0 +c5 f9 6e .. vmovd ${GPR_I32}, %xmm0 c4 e2 79 78 c0 vpbroadcastb %xmm0, %xmm0`], ['i32', 'i16x8.splat', ` -c5 f9 6e .. vmovd %e\\w+, %xmm0 +c5 f9 6e .. vmovd ${GPR_I32}, %xmm0 c4 e2 79 79 c0 vpbroadcastw %xmm0, %xmm0`], ['i32', 'i32x4.splat', ` -c5 f9 6e .. vmovd %e\\w+, %xmm0 +c5 f9 6e .. vmovd ${GPR_I32}, %xmm0 c4 e2 79 58 c0 vpbroadcastd %xmm0, %xmm0`], - ['f32', 'f32x4.splat', `c4 e2 79 18 c0 vbroadcastss %xmm0, %xmm0`]]); + ['i64', 'i64x2.splat', ` +c4 e1 f9 6e .. vmovq ${GPR_I64}, %xmm0 +c4 e2 79 59 c0 vpbroadcastq %xmm0, %xmm0`], + ['f32', 'f32x4.splat', `c4 e2 79 18 c0 vbroadcastss %xmm0, %xmm0`]], {log:true}); codegenTestX64_T_v128_avxhack( [['i32', 'v128.load8_splat', diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index e2051ce1fdd6..88a7d61be552 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -2224,14 +2224,23 @@ class MacroAssembler : public MacroAssemblerSpecific { // Replace lane value + inline void replaceLaneInt8x16(unsigned lane, FloatRegister lhs, Register rhs, + FloatRegister dest) DEFINED_ON(x86_shared); + inline void replaceLaneInt8x16(unsigned lane, Register rhs, FloatRegister lhsDest) DEFINED_ON(x86_shared, arm64); + inline void replaceLaneInt16x8(unsigned lane, FloatRegister lhs, Register rhs, + FloatRegister dest) DEFINED_ON(x86_shared); + inline void replaceLaneInt16x8(unsigned lane, Register rhs, FloatRegister lhsDest) DEFINED_ON(x86_shared, arm64); + inline void replaceLaneInt32x4(unsigned lane, FloatRegister lhs, Register rhs, + FloatRegister dest) DEFINED_ON(x86_shared); + inline void replaceLaneInt32x4(unsigned lane, Register rhs, FloatRegister lhsDest) DEFINED_ON(x86_shared, arm64); @@ -2244,10 +2253,18 @@ class MacroAssembler : public MacroAssemblerSpecific { FloatRegister lhsDest) DEFINED_ON(x86, x64, arm64); + inline void replaceLaneFloat32x4(unsigned lane, FloatRegister lhs, + FloatRegister rhs, FloatRegister dest) + DEFINED_ON(x86_shared); + inline void replaceLaneFloat32x4(unsigned lane, FloatRegister rhs, FloatRegister lhsDest) DEFINED_ON(x86_shared, arm64); + inline void replaceLaneFloat64x2(unsigned lane, FloatRegister lhs, + FloatRegister rhs, FloatRegister dest) + DEFINED_ON(x86_shared); + inline void replaceLaneFloat64x2(unsigned lane, FloatRegister rhs, FloatRegister lhsDest) DEFINED_ON(x86_shared, arm64); @@ -2393,94 +2410,64 @@ class MacroAssembler : public MacroAssemblerSpecific { // Integer Add - inline void addInt8x16(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void addInt8x16(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void addInt8x16(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); - inline void addInt16x8(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void addInt16x8(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void addInt16x8(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); - inline void addInt32x4(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void addInt32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void addInt32x4(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); - inline void addInt64x2(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void addInt64x2(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void addInt64x2(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); // Integer Subtract - inline void subInt8x16(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void subInt8x16(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void subInt8x16(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); - inline void subInt16x8(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void subInt16x8(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void subInt16x8(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); - inline void subInt32x4(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void subInt32x4(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); inline void subInt32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) DEFINED_ON(x86_shared, arm64); - inline void subInt64x2(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void subInt64x2(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); inline void subInt64x2(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) DEFINED_ON(x86_shared, arm64); // Integer Multiply - inline void mulInt16x8(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void mulInt16x8(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void mulInt16x8(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); - inline void mulInt32x4(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void mulInt32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) DEFINED_ON(x86_shared, arm64); @@ -2595,205 +2582,151 @@ class MacroAssembler : public MacroAssemblerSpecific { // Saturating integer add - inline void addSatInt8x16(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void addSatInt8x16(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void addSatInt8x16(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); - inline void unsignedAddSatInt8x16(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void unsignedAddSatInt8x16(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) + DEFINED_ON(x86_shared, arm64); inline void unsignedAddSatInt8x16(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); - inline void addSatInt16x8(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void addSatInt16x8(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void addSatInt16x8(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); - inline void unsignedAddSatInt16x8(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void unsignedAddSatInt16x8(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) + DEFINED_ON(x86_shared, arm64); inline void unsignedAddSatInt16x8(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); // Saturating integer subtract - inline void subSatInt8x16(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void subSatInt8x16(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void subSatInt8x16(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); - inline void unsignedSubSatInt8x16(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void unsignedSubSatInt8x16(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) + DEFINED_ON(x86_shared, arm64); inline void unsignedSubSatInt8x16(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); - inline void subSatInt16x8(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void subSatInt16x8(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void subSatInt16x8(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); - inline void unsignedSubSatInt16x8(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void unsignedSubSatInt16x8(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) + DEFINED_ON(x86_shared, arm64); inline void unsignedSubSatInt16x8(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); // Lane-wise integer minimum - inline void minInt8x16(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void minInt8x16(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void minInt8x16(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); - inline void unsignedMinInt8x16(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void unsignedMinInt8x16(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) + DEFINED_ON(x86_shared, arm64); inline void unsignedMinInt8x16(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); - inline void minInt16x8(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void minInt16x8(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void minInt16x8(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); - inline void unsignedMinInt16x8(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void unsignedMinInt16x8(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) + DEFINED_ON(x86_shared, arm64); inline void unsignedMinInt16x8(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); - inline void minInt32x4(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void minInt32x4(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void minInt32x4(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); - inline void unsignedMinInt32x4(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void unsignedMinInt32x4(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) + DEFINED_ON(x86_shared, arm64); inline void unsignedMinInt32x4(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); // Lane-wise integer maximum - inline void maxInt8x16(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void maxInt8x16(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void maxInt8x16(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); - inline void unsignedMaxInt8x16(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void unsignedMaxInt8x16(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) + DEFINED_ON(x86_shared, arm64); inline void unsignedMaxInt8x16(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); - inline void maxInt16x8(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void maxInt16x8(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void maxInt16x8(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); - inline void unsignedMaxInt16x8(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void unsignedMaxInt16x8(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) + DEFINED_ON(x86_shared, arm64); inline void unsignedMaxInt16x8(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); - inline void maxInt32x4(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void maxInt32x4(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void maxInt32x4(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); - inline void unsignedMaxInt32x4(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void unsignedMaxInt32x4(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) + DEFINED_ON(x86_shared, arm64); inline void unsignedMaxInt32x4(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); // Lane-wise integer rounding average - inline void unsignedAverageInt8x16(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void unsignedAverageInt8x16(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); - - inline void unsignedAverageInt16x8(FloatRegister rhs, FloatRegister lhsDest) + FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void unsignedAverageInt16x8(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) + DEFINED_ON(x86_shared, arm64); // Lane-wise integer absolute value @@ -3230,80 +3163,56 @@ class MacroAssembler : public MacroAssemblerSpecific { // Floating add - inline void addFloat32x4(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void addFloat32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void addFloat32x4(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); - inline void addFloat64x2(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void addFloat64x2(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void addFloat64x2(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); // Floating subtract - inline void subFloat32x4(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void subFloat32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void subFloat32x4(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); - inline void subFloat64x2(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void subFloat64x2(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void subFloat64x2(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); // Floating division - inline void divFloat32x4(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void divFloat32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void divFloat32x4(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); - inline void divFloat64x2(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void divFloat64x2(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void divFloat64x2(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); // Floating Multiply - inline void mulFloat32x4(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void mulFloat32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void mulFloat32x4(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); - inline void mulFloat64x2(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void mulFloat64x2(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void mulFloat64x2(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); @@ -3399,41 +3308,31 @@ class MacroAssembler : public MacroAssemblerSpecific { // Integer to integer narrowing - inline void narrowInt16x8(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void narrowInt16x8(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); inline void narrowInt16x8(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); - - inline void unsignedNarrowInt16x8(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); + FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void unsignedNarrowInt16x8(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); inline void unsignedNarrowInt16x8(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); - - inline void narrowInt32x4(FloatRegister rhs, FloatRegister lhsDest) + FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void narrowInt32x4(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); inline void narrowInt32x4(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); - - inline void unsignedNarrowInt32x4(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); + FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void unsignedNarrowInt32x4(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); inline void unsignedNarrowInt32x4(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) + DEFINED_ON(x86_shared, arm64); // Integer to integer widening @@ -3511,11 +3410,8 @@ class MacroAssembler : public MacroAssemblerSpecific { // Widening/pairwise integer dot product - inline void widenDotInt16x8(FloatRegister rhs, FloatRegister lhsDest) - DEFINED_ON(x86_shared, arm64); - inline void widenDotInt16x8(FloatRegister lhs, FloatRegister rhs, - FloatRegister dest) DEFINED_ON(arm64); + FloatRegister dest) DEFINED_ON(x86_shared, arm64); inline void widenDotInt16x8(FloatRegister lhs, const SimdConstant& rhs, FloatRegister dest) DEFINED_ON(x86_shared); diff --git a/js/src/jit/arm64/MacroAssembler-arm64-inl.h b/js/src/jit/arm64/MacroAssembler-arm64-inl.h index 0220c6c36891..c3eba2e90e43 100644 --- a/js/src/jit/arm64/MacroAssembler-arm64-inl.h +++ b/js/src/jit/arm64/MacroAssembler-arm64-inl.h @@ -2587,37 +2587,21 @@ void MacroAssembler::swizzleInt8x16Relaxed(FloatRegister lhs, FloatRegister rhs, // Integer Add -void MacroAssembler::addInt8x16(FloatRegister rhs, FloatRegister lhsDest) { - Add(Simd16B(lhsDest), Simd16B(lhsDest), Simd16B(rhs)); -} - void MacroAssembler::addInt8x16(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Add(Simd16B(dest), Simd16B(lhs), Simd16B(rhs)); } -void MacroAssembler::addInt16x8(FloatRegister rhs, FloatRegister lhsDest) { - Add(Simd8H(lhsDest), Simd8H(lhsDest), Simd8H(rhs)); -} - void MacroAssembler::addInt16x8(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Add(Simd8H(dest), Simd8H(lhs), Simd8H(rhs)); } -void MacroAssembler::addInt32x4(FloatRegister rhs, FloatRegister lhsDest) { - Add(Simd4S(lhsDest), Simd4S(lhsDest), Simd4S(rhs)); -} - void MacroAssembler::addInt32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Add(Simd4S(dest), Simd4S(lhs), Simd4S(rhs)); } -void MacroAssembler::addInt64x2(FloatRegister rhs, FloatRegister lhsDest) { - Add(Simd2D(lhsDest), Simd2D(lhsDest), Simd2D(rhs)); -} - void MacroAssembler::addInt64x2(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Add(Simd2D(dest), Simd2D(lhs), Simd2D(rhs)); @@ -2625,37 +2609,21 @@ void MacroAssembler::addInt64x2(FloatRegister lhs, FloatRegister rhs, // Integer Subtract -void MacroAssembler::subInt8x16(FloatRegister rhs, FloatRegister lhsDest) { - Sub(Simd16B(lhsDest), Simd16B(lhsDest), Simd16B(rhs)); -} - void MacroAssembler::subInt8x16(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Sub(Simd16B(dest), Simd16B(lhs), Simd16B(rhs)); } -void MacroAssembler::subInt16x8(FloatRegister rhs, FloatRegister lhsDest) { - Sub(Simd8H(lhsDest), Simd8H(lhsDest), Simd8H(rhs)); -} - void MacroAssembler::subInt16x8(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Sub(Simd8H(dest), Simd8H(lhs), Simd8H(rhs)); } -void MacroAssembler::subInt32x4(FloatRegister rhs, FloatRegister lhsDest) { - Sub(Simd4S(lhsDest), Simd4S(lhsDest), Simd4S(rhs)); -} - void MacroAssembler::subInt32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Sub(Simd4S(dest), Simd4S(lhs), Simd4S(rhs)); } -void MacroAssembler::subInt64x2(FloatRegister rhs, FloatRegister lhsDest) { - Sub(Simd2D(lhsDest), Simd2D(lhsDest), Simd2D(rhs)); -} - void MacroAssembler::subInt64x2(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Sub(Simd2D(dest), Simd2D(lhs), Simd2D(rhs)); @@ -2663,19 +2631,11 @@ void MacroAssembler::subInt64x2(FloatRegister lhs, FloatRegister rhs, // Integer Multiply -void MacroAssembler::mulInt16x8(FloatRegister rhs, FloatRegister lhsDest) { - Mul(Simd8H(lhsDest), Simd8H(lhsDest), Simd8H(rhs)); -} - void MacroAssembler::mulInt16x8(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Mul(Simd8H(dest), Simd8H(lhs), Simd8H(rhs)); } -void MacroAssembler::mulInt32x4(FloatRegister rhs, FloatRegister lhsDest) { - Mul(Simd4S(lhsDest), Simd4S(lhsDest), Simd4S(rhs)); -} - void MacroAssembler::mulInt32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Mul(Simd4S(dest), Simd4S(lhs), Simd4S(rhs)); @@ -2855,39 +2815,21 @@ void MacroAssembler::negInt64x2(FloatRegister src, FloatRegister dest) { // Saturating integer add -void MacroAssembler::addSatInt8x16(FloatRegister rhs, FloatRegister lhsDest) { - Sqadd(Simd16B(lhsDest), Simd16B(lhsDest), Simd16B(rhs)); -} - void MacroAssembler::addSatInt8x16(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Sqadd(Simd16B(dest), Simd16B(lhs), Simd16B(rhs)); } -void MacroAssembler::unsignedAddSatInt8x16(FloatRegister rhs, - FloatRegister lhsDest) { - Uqadd(Simd16B(lhsDest), Simd16B(lhsDest), Simd16B(rhs)); -} - void MacroAssembler::unsignedAddSatInt8x16(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Uqadd(Simd16B(dest), Simd16B(lhs), Simd16B(rhs)); } -void MacroAssembler::addSatInt16x8(FloatRegister rhs, FloatRegister lhsDest) { - Sqadd(Simd8H(lhsDest), Simd8H(lhsDest), Simd8H(rhs)); -} - void MacroAssembler::addSatInt16x8(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Sqadd(Simd8H(dest), Simd8H(lhs), Simd8H(rhs)); } -void MacroAssembler::unsignedAddSatInt16x8(FloatRegister rhs, - FloatRegister lhsDest) { - Uqadd(Simd8H(lhsDest), Simd8H(lhsDest), Simd8H(rhs)); -} - void MacroAssembler::unsignedAddSatInt16x8(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Uqadd(Simd8H(dest), Simd8H(lhs), Simd8H(rhs)); @@ -2895,39 +2837,21 @@ void MacroAssembler::unsignedAddSatInt16x8(FloatRegister lhs, FloatRegister rhs, // Saturating integer subtract -void MacroAssembler::subSatInt8x16(FloatRegister rhs, FloatRegister lhsDest) { - Sqsub(Simd16B(lhsDest), Simd16B(lhsDest), Simd16B(rhs)); -} - void MacroAssembler::subSatInt8x16(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Sqsub(Simd16B(dest), Simd16B(lhs), Simd16B(rhs)); } -void MacroAssembler::unsignedSubSatInt8x16(FloatRegister rhs, - FloatRegister lhsDest) { - Uqsub(Simd16B(lhsDest), Simd16B(lhsDest), Simd16B(rhs)); -} - void MacroAssembler::unsignedSubSatInt8x16(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Uqsub(Simd16B(dest), Simd16B(lhs), Simd16B(rhs)); } -void MacroAssembler::subSatInt16x8(FloatRegister rhs, FloatRegister lhsDest) { - Sqsub(Simd8H(lhsDest), Simd8H(lhsDest), Simd8H(rhs)); -} - void MacroAssembler::subSatInt16x8(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Sqsub(Simd8H(dest), Simd8H(lhs), Simd8H(rhs)); } -void MacroAssembler::unsignedSubSatInt16x8(FloatRegister rhs, - FloatRegister lhsDest) { - Uqsub(Simd8H(lhsDest), Simd8H(lhsDest), Simd8H(rhs)); -} - void MacroAssembler::unsignedSubSatInt16x8(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Uqsub(Simd8H(dest), Simd8H(lhs), Simd8H(rhs)); @@ -2935,58 +2859,31 @@ void MacroAssembler::unsignedSubSatInt16x8(FloatRegister lhs, FloatRegister rhs, // Lane-wise integer minimum -void MacroAssembler::minInt8x16(FloatRegister rhs, FloatRegister lhsDest) { - Smin(Simd16B(lhsDest), Simd16B(lhsDest), Simd16B(rhs)); -} - void MacroAssembler::minInt8x16(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Smin(Simd16B(dest), Simd16B(lhs), Simd16B(rhs)); } -void MacroAssembler::unsignedMinInt8x16(FloatRegister rhs, - FloatRegister lhsDest) { - Umin(Simd16B(lhsDest), Simd16B(lhsDest), Simd16B(rhs)); -} - void MacroAssembler::unsignedMinInt8x16(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Umin(Simd16B(dest), Simd16B(lhs), Simd16B(rhs)); } -void MacroAssembler::minInt16x8(FloatRegister rhs, FloatRegister lhsDest) { - Smin(Simd8H(lhsDest), Simd8H(lhsDest), Simd8H(rhs)); -} - void MacroAssembler::minInt16x8(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Smin(Simd8H(dest), Simd8H(lhs), Simd8H(rhs)); } -void MacroAssembler::unsignedMinInt16x8(FloatRegister rhs, - FloatRegister lhsDest) { - Umin(Simd8H(lhsDest), Simd8H(lhsDest), Simd8H(rhs)); -} - void MacroAssembler::unsignedMinInt16x8(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Umin(Simd8H(dest), Simd8H(lhs), Simd8H(rhs)); } -void MacroAssembler::minInt32x4(FloatRegister rhs, FloatRegister lhsDest) { - Smin(Simd4S(lhsDest), Simd4S(lhsDest), Simd4S(rhs)); -} - void MacroAssembler::minInt32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Smin(Simd4S(dest), Simd4S(lhs), Simd4S(rhs)); } -void MacroAssembler::unsignedMinInt32x4(FloatRegister rhs, - FloatRegister lhsDest) { - Umin(Simd4S(lhsDest), Simd4S(lhsDest), Simd4S(rhs)); -} - void MacroAssembler::unsignedMinInt32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Umin(Simd4S(dest), Simd4S(lhs), Simd4S(rhs)); @@ -2994,58 +2891,31 @@ void MacroAssembler::unsignedMinInt32x4(FloatRegister lhs, FloatRegister rhs, // Lane-wise integer maximum -void MacroAssembler::maxInt8x16(FloatRegister rhs, FloatRegister lhsDest) { - Smax(Simd16B(lhsDest), Simd16B(lhsDest), Simd16B(rhs)); -} - void MacroAssembler::maxInt8x16(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Smax(Simd16B(dest), Simd16B(lhs), Simd16B(rhs)); } -void MacroAssembler::unsignedMaxInt8x16(FloatRegister rhs, - FloatRegister lhsDest) { - Umax(Simd16B(lhsDest), Simd16B(lhsDest), Simd16B(rhs)); -} - void MacroAssembler::unsignedMaxInt8x16(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Umax(Simd16B(dest), Simd16B(lhs), Simd16B(rhs)); } -void MacroAssembler::maxInt16x8(FloatRegister rhs, FloatRegister lhsDest) { - Smax(Simd8H(lhsDest), Simd8H(lhsDest), Simd8H(rhs)); -} - void MacroAssembler::maxInt16x8(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Smax(Simd8H(dest), Simd8H(lhs), Simd8H(rhs)); } -void MacroAssembler::unsignedMaxInt16x8(FloatRegister rhs, - FloatRegister lhsDest) { - Umax(Simd8H(lhsDest), Simd8H(lhsDest), Simd8H(rhs)); -} - void MacroAssembler::unsignedMaxInt16x8(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Umax(Simd8H(dest), Simd8H(lhs), Simd8H(rhs)); } -void MacroAssembler::maxInt32x4(FloatRegister rhs, FloatRegister lhsDest) { - Smax(Simd4S(lhsDest), Simd4S(lhsDest), Simd4S(rhs)); -} - void MacroAssembler::maxInt32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Smax(Simd4S(dest), Simd4S(lhs), Simd4S(rhs)); } -void MacroAssembler::unsignedMaxInt32x4(FloatRegister rhs, - FloatRegister lhsDest) { - Umax(Simd4S(lhsDest), Simd4S(lhsDest), Simd4S(rhs)); -} - void MacroAssembler::unsignedMaxInt32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Umax(Simd4S(dest), Simd4S(lhs), Simd4S(rhs)); @@ -3053,22 +2923,12 @@ void MacroAssembler::unsignedMaxInt32x4(FloatRegister lhs, FloatRegister rhs, // Lane-wise integer rounding average -void MacroAssembler::unsignedAverageInt8x16(FloatRegister rhs, - FloatRegister lhsDest) { - Urhadd(Simd16B(lhsDest), Simd16B(lhsDest), Simd16B(rhs)); -} - void MacroAssembler::unsignedAverageInt8x16(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Urhadd(Simd16B(dest), Simd16B(lhs), Simd16B(rhs)); } -void MacroAssembler::unsignedAverageInt16x8(FloatRegister rhs, - FloatRegister lhsDest) { - Urhadd(Simd8H(lhsDest), Simd8H(lhsDest), Simd8H(rhs)); -} - void MacroAssembler::unsignedAverageInt16x8(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { @@ -3560,19 +3420,11 @@ void MacroAssembler::maxFloat64x2(FloatRegister rhs, FloatRegister lhsDest) { // Floating add -void MacroAssembler::addFloat32x4(FloatRegister rhs, FloatRegister lhsDest) { - Fadd(Simd4S(lhsDest), Simd4S(lhsDest), Simd4S(rhs)); -} - void MacroAssembler::addFloat32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Fadd(Simd4S(dest), Simd4S(lhs), Simd4S(rhs)); } -void MacroAssembler::addFloat64x2(FloatRegister rhs, FloatRegister lhsDest) { - Fadd(Simd2D(lhsDest), Simd2D(lhsDest), Simd2D(rhs)); -} - void MacroAssembler::addFloat64x2(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Fadd(Simd2D(dest), Simd2D(lhs), Simd2D(rhs)); @@ -3580,19 +3432,11 @@ void MacroAssembler::addFloat64x2(FloatRegister lhs, FloatRegister rhs, // Floating subtract -void MacroAssembler::subFloat32x4(FloatRegister rhs, FloatRegister lhsDest) { - Fsub(Simd4S(lhsDest), Simd4S(lhsDest), Simd4S(rhs)); -} - void MacroAssembler::subFloat32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Fsub(Simd4S(dest), Simd4S(lhs), Simd4S(rhs)); } -void MacroAssembler::subFloat64x2(FloatRegister rhs, FloatRegister lhsDest) { - Fsub(Simd2D(lhsDest), Simd2D(lhsDest), Simd2D(rhs)); -} - void MacroAssembler::subFloat64x2(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Fsub(Simd2D(dest), Simd2D(lhs), Simd2D(rhs)); @@ -3600,19 +3444,11 @@ void MacroAssembler::subFloat64x2(FloatRegister lhs, FloatRegister rhs, // Floating division -void MacroAssembler::divFloat32x4(FloatRegister rhs, FloatRegister lhsDest) { - Fdiv(Simd4S(lhsDest), Simd4S(lhsDest), Simd4S(rhs)); -} - void MacroAssembler::divFloat32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Fdiv(Simd4S(dest), Simd4S(lhs), Simd4S(rhs)); } -void MacroAssembler::divFloat64x2(FloatRegister rhs, FloatRegister lhsDest) { - Fdiv(Simd2D(lhsDest), Simd2D(lhsDest), Simd2D(rhs)); -} - void MacroAssembler::divFloat64x2(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Fdiv(Simd2D(dest), Simd2D(lhs), Simd2D(rhs)); @@ -3620,19 +3456,11 @@ void MacroAssembler::divFloat64x2(FloatRegister lhs, FloatRegister rhs, // Floating Multiply -void MacroAssembler::mulFloat32x4(FloatRegister rhs, FloatRegister lhsDest) { - Fmul(Simd4S(lhsDest), Simd4S(lhsDest), Simd4S(rhs)); -} - void MacroAssembler::mulFloat32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Fmul(Simd4S(dest), Simd4S(lhs), Simd4S(rhs)); } -void MacroAssembler::mulFloat64x2(FloatRegister rhs, FloatRegister lhsDest) { - Fmul(Simd2D(lhsDest), Simd2D(lhsDest), Simd2D(rhs)); -} - void MacroAssembler::mulFloat64x2(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { Fmul(Simd2D(dest), Simd2D(lhs), Simd2D(rhs)); @@ -3758,16 +3586,6 @@ void MacroAssembler::convertFloat32x4ToFloat64x2(FloatRegister src, // Integer to integer narrowing -void MacroAssembler::narrowInt16x8(FloatRegister rhs, FloatRegister lhsDest) { - ScratchSimd128Scope scratch(*this); - if (rhs == lhsDest) { - Mov(scratch, SimdReg(rhs)); - rhs = scratch; - } - Sqxtn(Simd8B(lhsDest), Simd8H(lhsDest)); - Sqxtn2(Simd16B(lhsDest), Simd8H(rhs)); -} - void MacroAssembler::narrowInt16x8(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { ScratchSimd128Scope scratch(*this); @@ -3779,17 +3597,6 @@ void MacroAssembler::narrowInt16x8(FloatRegister lhs, FloatRegister rhs, Sqxtn2(Simd16B(dest), Simd8H(rhs)); } -void MacroAssembler::unsignedNarrowInt16x8(FloatRegister rhs, - FloatRegister lhsDest) { - ScratchSimd128Scope scratch(*this); - if (rhs == lhsDest) { - Mov(scratch, SimdReg(rhs)); - rhs = scratch; - } - Sqxtun(Simd8B(lhsDest), Simd8H(lhsDest)); - Sqxtun2(Simd16B(lhsDest), Simd8H(rhs)); -} - void MacroAssembler::unsignedNarrowInt16x8(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { ScratchSimd128Scope scratch(*this); @@ -3801,16 +3608,6 @@ void MacroAssembler::unsignedNarrowInt16x8(FloatRegister lhs, FloatRegister rhs, Sqxtun2(Simd16B(dest), Simd8H(rhs)); } -void MacroAssembler::narrowInt32x4(FloatRegister rhs, FloatRegister lhsDest) { - ScratchSimd128Scope scratch(*this); - if (rhs == lhsDest) { - Mov(scratch, SimdReg(rhs)); - rhs = scratch; - } - Sqxtn(Simd4H(lhsDest), Simd4S(lhsDest)); - Sqxtn2(Simd8H(lhsDest), Simd4S(rhs)); -} - void MacroAssembler::narrowInt32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { ScratchSimd128Scope scratch(*this); @@ -3822,17 +3619,6 @@ void MacroAssembler::narrowInt32x4(FloatRegister lhs, FloatRegister rhs, Sqxtn2(Simd8H(dest), Simd4S(rhs)); } -void MacroAssembler::unsignedNarrowInt32x4(FloatRegister rhs, - FloatRegister lhsDest) { - ScratchSimd128Scope scratch(*this); - if (rhs == lhsDest) { - Mov(scratch, SimdReg(rhs)); - rhs = scratch; - } - Sqxtun(Simd4H(lhsDest), Simd4S(lhsDest)); - Sqxtun2(Simd8H(lhsDest), Simd4S(rhs)); -} - void MacroAssembler::unsignedNarrowInt32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { ScratchSimd128Scope scratch(*this); @@ -3980,10 +3766,6 @@ void MacroAssembler::pseudoMaxFloat64x2(FloatRegister lhs, FloatRegister rhs, // Widening/pairwise integer dot product (experimental as of August, 2020) // https://github.com/WebAssembly/simd/pull/127 -void MacroAssembler::widenDotInt16x8(FloatRegister rhs, FloatRegister lhsDest) { - widenDotInt16x8(lhsDest, rhs, lhsDest); -} - void MacroAssembler::widenDotInt16x8(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { ScratchSimd128Scope scratch(*this); diff --git a/js/src/jit/x64/MacroAssembler-x64-inl.h b/js/src/jit/x64/MacroAssembler-x64-inl.h index 1677b150ecca..d3cee30311b1 100644 --- a/js/src/jit/x64/MacroAssembler-x64-inl.h +++ b/js/src/jit/x64/MacroAssembler-x64-inl.h @@ -926,8 +926,12 @@ void MacroAssembler::replaceLaneInt64x2(unsigned lane, FloatRegister lhs, // Splat void MacroAssembler::splatX2(Register64 src, FloatRegister dest) { - vpinsrq(0, src.reg, dest, dest); - vpinsrq(1, src.reg, dest, dest); + vmovq(src.reg, dest); + if (HasAVX2()) { + vbroadcastq(Operand(dest), dest); + } else { + vpunpcklqdq(dest, dest, dest); + } } // ======================================================================== diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index fc66d0308be5..dca0b7c57ffe 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -2333,73 +2333,73 @@ void CodeGenerator::visitWasmBinarySimd128(LWasmBinarySimd128* ins) { masm.bitwiseNotAndSimd128(lhs, rhs, dest); break; case wasm::SimdOp::I8x16AvgrU: - masm.unsignedAverageInt8x16(rhs, lhsDest); + masm.unsignedAverageInt8x16(lhs, rhs, dest); break; case wasm::SimdOp::I16x8AvgrU: - masm.unsignedAverageInt16x8(rhs, lhsDest); + masm.unsignedAverageInt16x8(lhs, rhs, dest); break; case wasm::SimdOp::I8x16Add: - masm.addInt8x16(rhs, lhsDest); + masm.addInt8x16(lhs, rhs, dest); break; case wasm::SimdOp::I8x16AddSatS: - masm.addSatInt8x16(rhs, lhsDest); + masm.addSatInt8x16(lhs, rhs, dest); break; case wasm::SimdOp::I8x16AddSatU: - masm.unsignedAddSatInt8x16(rhs, lhsDest); + masm.unsignedAddSatInt8x16(lhs, rhs, dest); break; case wasm::SimdOp::I8x16Sub: - masm.subInt8x16(rhs, lhsDest); + masm.subInt8x16(lhs, rhs, dest); break; case wasm::SimdOp::I8x16SubSatS: - masm.subSatInt8x16(rhs, lhsDest); + masm.subSatInt8x16(lhs, rhs, dest); break; case wasm::SimdOp::I8x16SubSatU: - masm.unsignedSubSatInt8x16(rhs, lhsDest); + masm.unsignedSubSatInt8x16(lhs, rhs, dest); break; case wasm::SimdOp::I8x16MinS: - masm.minInt8x16(rhs, lhsDest); + masm.minInt8x16(lhs, rhs, dest); break; case wasm::SimdOp::I8x16MinU: - masm.unsignedMinInt8x16(rhs, lhsDest); + masm.unsignedMinInt8x16(lhs, rhs, dest); break; case wasm::SimdOp::I8x16MaxS: - masm.maxInt8x16(rhs, lhsDest); + masm.maxInt8x16(lhs, rhs, dest); break; case wasm::SimdOp::I8x16MaxU: - masm.unsignedMaxInt8x16(rhs, lhsDest); + masm.unsignedMaxInt8x16(lhs, rhs, dest); break; case wasm::SimdOp::I16x8Add: - masm.addInt16x8(rhs, lhsDest); + masm.addInt16x8(lhs, rhs, dest); break; case wasm::SimdOp::I16x8AddSatS: - masm.addSatInt16x8(rhs, lhsDest); + masm.addSatInt16x8(lhs, rhs, dest); break; case wasm::SimdOp::I16x8AddSatU: - masm.unsignedAddSatInt16x8(rhs, lhsDest); + masm.unsignedAddSatInt16x8(lhs, rhs, dest); break; case wasm::SimdOp::I16x8Sub: - masm.subInt16x8(rhs, lhsDest); + masm.subInt16x8(lhs, rhs, dest); break; case wasm::SimdOp::I16x8SubSatS: - masm.subSatInt16x8(rhs, lhsDest); + masm.subSatInt16x8(lhs, rhs, dest); break; case wasm::SimdOp::I16x8SubSatU: - masm.unsignedSubSatInt16x8(rhs, lhsDest); + masm.unsignedSubSatInt16x8(lhs, rhs, dest); break; case wasm::SimdOp::I16x8Mul: - masm.mulInt16x8(rhs, lhsDest); + masm.mulInt16x8(lhs, rhs, dest); break; case wasm::SimdOp::I16x8MinS: - masm.minInt16x8(rhs, lhsDest); + masm.minInt16x8(lhs, rhs, dest); break; case wasm::SimdOp::I16x8MinU: - masm.unsignedMinInt16x8(rhs, lhsDest); + masm.unsignedMinInt16x8(lhs, rhs, dest); break; case wasm::SimdOp::I16x8MaxS: - masm.maxInt16x8(rhs, lhsDest); + masm.maxInt16x8(lhs, rhs, dest); break; case wasm::SimdOp::I16x8MaxU: - masm.unsignedMaxInt16x8(rhs, lhsDest); + masm.unsignedMaxInt16x8(lhs, rhs, dest); break; case wasm::SimdOp::I32x4Add: masm.addInt32x4(lhs, rhs, dest); @@ -2411,25 +2411,25 @@ void CodeGenerator::visitWasmBinarySimd128(LWasmBinarySimd128* ins) { masm.mulInt32x4(lhs, rhs, dest); break; case wasm::SimdOp::I32x4MinS: - masm.minInt32x4(rhs, lhsDest); + masm.minInt32x4(lhs, rhs, dest); break; case wasm::SimdOp::I32x4MinU: - masm.unsignedMinInt32x4(rhs, lhsDest); + masm.unsignedMinInt32x4(lhs, rhs, dest); break; case wasm::SimdOp::I32x4MaxS: - masm.maxInt32x4(rhs, lhsDest); + masm.maxInt32x4(lhs, rhs, dest); break; case wasm::SimdOp::I32x4MaxU: - masm.unsignedMaxInt32x4(rhs, lhsDest); + masm.unsignedMaxInt32x4(lhs, rhs, dest); break; case wasm::SimdOp::I64x2Add: - masm.addInt64x2(rhs, lhsDest); + masm.addInt64x2(lhs, rhs, dest); break; case wasm::SimdOp::I64x2Sub: - masm.subInt64x2(rhs, lhsDest); + masm.subInt64x2(lhs, rhs, dest); break; case wasm::SimdOp::I64x2Mul: - masm.mulInt64x2(lhsDest, rhs, lhsDest, temp1); + masm.mulInt64x2(lhs, rhs, dest, temp1); break; case wasm::SimdOp::F32x4Add: masm.addFloat32x4(lhs, rhs, dest); @@ -2450,16 +2450,16 @@ void CodeGenerator::visitWasmBinarySimd128(LWasmBinarySimd128* ins) { masm.maxFloat32x4(lhs, rhs, dest, temp1, temp2); break; case wasm::SimdOp::F64x2Add: - masm.addFloat64x2(rhs, lhsDest); + masm.addFloat64x2(lhs, rhs, dest); break; case wasm::SimdOp::F64x2Sub: - masm.subFloat64x2(rhs, lhsDest); + masm.subFloat64x2(lhs, rhs, dest); break; case wasm::SimdOp::F64x2Mul: - masm.mulFloat64x2(rhs, lhsDest); + masm.mulFloat64x2(lhs, rhs, dest); break; case wasm::SimdOp::F64x2Div: - masm.divFloat64x2(rhs, lhsDest); + masm.divFloat64x2(lhs, rhs, dest); break; case wasm::SimdOp::F64x2Min: masm.minFloat64x2(lhs, rhs, dest, temp1, temp2); @@ -2474,16 +2474,16 @@ void CodeGenerator::visitWasmBinarySimd128(LWasmBinarySimd128* ins) { masm.swizzleInt8x16Relaxed(rhs, lhsDest); break; case wasm::SimdOp::I8x16NarrowI16x8S: - masm.narrowInt16x8(rhs, lhsDest); + masm.narrowInt16x8(lhs, rhs, dest); break; case wasm::SimdOp::I8x16NarrowI16x8U: - masm.unsignedNarrowInt16x8(rhs, lhsDest); + masm.unsignedNarrowInt16x8(lhs, rhs, dest); break; case wasm::SimdOp::I16x8NarrowI32x4S: - masm.narrowInt32x4(rhs, lhsDest); + masm.narrowInt32x4(lhs, rhs, dest); break; case wasm::SimdOp::I16x8NarrowI32x4U: - masm.unsignedNarrowInt32x4(rhs, lhsDest); + masm.unsignedNarrowInt32x4(lhs, rhs, dest); break; case wasm::SimdOp::I8x16Eq: masm.compareInt8x16(Assembler::Equal, rhs, lhsDest); @@ -2638,7 +2638,7 @@ void CodeGenerator::visitWasmBinarySimd128(LWasmBinarySimd128* ins) { masm.pseudoMinFloat64x2(lhsDest, rhs); break; case wasm::SimdOp::I32x4DotI16x8S: - masm.widenDotInt16x8(rhs, lhsDest); + masm.widenDotInt16x8(lhs, rhs, dest); break; case wasm::SimdOp::I16x8ExtmulLowI8x16S: masm.extMulLowInt8x16(rhs, lhsDest); @@ -3384,25 +3384,26 @@ void CodeGenerator::visitWasmPermuteSimd128(LWasmPermuteSimd128* ins) { void CodeGenerator::visitWasmReplaceLaneSimd128(LWasmReplaceLaneSimd128* ins) { #ifdef ENABLE_WASM_SIMD - FloatRegister lhsDest = ToFloatRegister(ins->lhsDest()); + FloatRegister lhs = ToFloatRegister(ins->lhsDest()); + FloatRegister dest = ToFloatRegister(ins->output()); const LAllocation* rhs = ins->rhs(); uint32_t laneIndex = ins->laneIndex(); switch (ins->simdOp()) { case wasm::SimdOp::I8x16ReplaceLane: - masm.replaceLaneInt8x16(laneIndex, ToRegister(rhs), lhsDest); + masm.replaceLaneInt8x16(laneIndex, lhs, ToRegister(rhs), dest); break; case wasm::SimdOp::I16x8ReplaceLane: - masm.replaceLaneInt16x8(laneIndex, ToRegister(rhs), lhsDest); + masm.replaceLaneInt16x8(laneIndex, lhs, ToRegister(rhs), dest); break; case wasm::SimdOp::I32x4ReplaceLane: - masm.replaceLaneInt32x4(laneIndex, ToRegister(rhs), lhsDest); + masm.replaceLaneInt32x4(laneIndex, lhs, ToRegister(rhs), dest); break; case wasm::SimdOp::F32x4ReplaceLane: - masm.replaceLaneFloat32x4(laneIndex, ToFloatRegister(rhs), lhsDest); + masm.replaceLaneFloat32x4(laneIndex, lhs, ToFloatRegister(rhs), dest); break; case wasm::SimdOp::F64x2ReplaceLane: - masm.replaceLaneFloat64x2(laneIndex, ToFloatRegister(rhs), lhsDest); + masm.replaceLaneFloat64x2(laneIndex, lhs, ToFloatRegister(rhs), dest); break; default: MOZ_CRASH("ReplaceLane SimdOp not implemented"); diff --git a/js/src/jit/x86-shared/Lowering-x86-shared.cpp b/js/src/jit/x86-shared/Lowering-x86-shared.cpp index d9b673967bba..3a9d63cc0211 100644 --- a/js/src/jit/x86-shared/Lowering-x86-shared.cpp +++ b/js/src/jit/x86-shared/Lowering-x86-shared.cpp @@ -995,13 +995,37 @@ void LIRGenerator::visitWasmBinarySimd128(MWasmBinarySimd128* ins) { // useRegisterAtStart is applied for both operands and no need for ReuseInput. switch (ins->simdOp()) { + case wasm::SimdOp::I8x16AvgrU: + case wasm::SimdOp::I16x8AvgrU: + case wasm::SimdOp::I8x16Add: + case wasm::SimdOp::I8x16AddSatS: + case wasm::SimdOp::I8x16AddSatU: + case wasm::SimdOp::I8x16Sub: + case wasm::SimdOp::I8x16SubSatS: + case wasm::SimdOp::I8x16SubSatU: + case wasm::SimdOp::I16x8Mul: + case wasm::SimdOp::I16x8MinS: + case wasm::SimdOp::I16x8MinU: + case wasm::SimdOp::I16x8MaxS: + case wasm::SimdOp::I16x8MaxU: case wasm::SimdOp::I32x4Add: case wasm::SimdOp::I32x4Sub: case wasm::SimdOp::I32x4Mul: + case wasm::SimdOp::I32x4MinS: + case wasm::SimdOp::I32x4MinU: + case wasm::SimdOp::I32x4MaxS: + case wasm::SimdOp::I32x4MaxU: + case wasm::SimdOp::I64x2Add: + case wasm::SimdOp::I64x2Sub: + case wasm::SimdOp::I64x2Mul: case wasm::SimdOp::F32x4Add: case wasm::SimdOp::F32x4Sub: case wasm::SimdOp::F32x4Mul: case wasm::SimdOp::F32x4Div: + case wasm::SimdOp::F64x2Add: + case wasm::SimdOp::F64x2Sub: + case wasm::SimdOp::F64x2Mul: + case wasm::SimdOp::F64x2Div: case wasm::SimdOp::F32x4Eq: case wasm::SimdOp::F32x4Ne: case wasm::SimdOp::F32x4Lt: @@ -1022,6 +1046,11 @@ void LIRGenerator::visitWasmBinarySimd128(MWasmBinarySimd128* ins) { case wasm::SimdOp::F32x4Max: case wasm::SimdOp::F64x2Min: case wasm::SimdOp::F64x2Max: + case wasm::SimdOp::I8x16NarrowI16x8S: + case wasm::SimdOp::I8x16NarrowI16x8U: + case wasm::SimdOp::I16x8NarrowI32x4S: + case wasm::SimdOp::I16x8NarrowI32x4U: + case wasm::SimdOp::I32x4DotI16x8S: if (isThreeOpAllowed()) { auto* lir = new (alloc()) LWasmBinarySimd128(op, useRegisterAtStart(lhs), @@ -1472,9 +1501,15 @@ void LIRGenerator::visitWasmReplaceLaneSimd128(MWasmReplaceLaneSimd128* ins) { defineReuseInput(lir, ins, LWasmReplaceInt64LaneSimd128::LhsDest); } } else { - auto* lir = new (alloc()) LWasmReplaceLaneSimd128( - useRegisterAtStart(ins->lhs()), useRegister(ins->rhs())); - defineReuseInput(lir, ins, LWasmReplaceLaneSimd128::LhsDest); + if (isThreeOpAllowed()) { + auto* lir = new (alloc()) LWasmReplaceLaneSimd128( + useRegisterAtStart(ins->lhs()), useRegisterAtStart(ins->rhs())); + define(lir, ins); + } else { + auto* lir = new (alloc()) LWasmReplaceLaneSimd128( + useRegisterAtStart(ins->lhs()), useRegister(ins->rhs())); + defineReuseInput(lir, ins, LWasmReplaceLaneSimd128::LhsDest); + } } #else MOZ_CRASH("No SIMD"); diff --git a/js/src/jit/x86-shared/MacroAssembler-x86-shared-SIMD.cpp b/js/src/jit/x86-shared/MacroAssembler-x86-shared-SIMD.cpp index c64b5e49fe94..61ce817a3918 100644 --- a/js/src/jit/x86-shared/MacroAssembler-x86-shared-SIMD.cpp +++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared-SIMD.cpp @@ -133,40 +133,44 @@ void MacroAssemblerX86Shared::extractLaneInt8x16(FloatRegister input, } } -void MacroAssemblerX86Shared::replaceLaneFloat32x4(FloatRegister rhs, - FloatRegister lhsDest, - unsigned lane) { - MOZ_ASSERT(lhsDest.isSimd128() && rhs.isSingle()); +void MacroAssemblerX86Shared::replaceLaneFloat32x4(unsigned lane, + FloatRegister lhs, + FloatRegister rhs, + FloatRegister dest) { + MOZ_ASSERT(lhs.isSimd128() && rhs.isSingle()); if (lane == 0) { - if (rhs.asSimd128() == lhsDest) { + if (rhs.asSimd128() == lhs) { // no-op, although this should not normally happen for type checking // reasons higher up in the stack. + moveSimd128Float(lhs, dest); } else { // move low dword of value into low dword of output - vmovss(rhs, lhsDest, lhsDest); + vmovss(rhs, lhs, dest); } } else { - vinsertps(vinsertpsMask(0, lane), rhs, lhsDest, lhsDest); + vinsertps(vinsertpsMask(0, lane), rhs, lhs, dest); } } -void MacroAssemblerX86Shared::replaceLaneFloat64x2(FloatRegister rhs, - FloatRegister lhsDest, - unsigned lane) { - MOZ_ASSERT(lhsDest.isSimd128() && rhs.isDouble()); +void MacroAssemblerX86Shared::replaceLaneFloat64x2(unsigned lane, + FloatRegister lhs, + FloatRegister rhs, + FloatRegister dest) { + MOZ_ASSERT(lhs.isSimd128() && rhs.isDouble()); if (lane == 0) { - if (rhs.asSimd128() == lhsDest) { + if (rhs.asSimd128() == lhs) { // no-op, although this should not normally happen for type checking // reasons higher up in the stack. + moveSimd128Float(lhs, dest); } else { // move low qword of value into low qword of output - vmovsd(rhs, lhsDest, lhsDest); + vmovsd(rhs, lhs, dest); } } else { // move low qword of value into high qword of output - vshufpd(0, rhs, lhsDest, lhsDest); + vshufpd(0, rhs, lhs, dest); } } diff --git a/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h b/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h index e11a80131945..6ca297583fbd 100644 --- a/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h +++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h @@ -1323,29 +1323,56 @@ void MacroAssembler::extractLaneFloat64x2(uint32_t lane, FloatRegister src, // Replace lane value +void MacroAssembler::replaceLaneInt8x16(unsigned lane, FloatRegister lhs, + Register rhs, FloatRegister dest) { + vpinsrb(lane, Operand(rhs), lhs, dest); +} + void MacroAssembler::replaceLaneInt8x16(unsigned lane, Register rhs, FloatRegister lhsDest) { vpinsrb(lane, Operand(rhs), lhsDest, lhsDest); } +void MacroAssembler::replaceLaneInt16x8(unsigned lane, FloatRegister lhs, + Register rhs, FloatRegister dest) { + vpinsrw(lane, Operand(rhs), lhs, dest); +} + void MacroAssembler::replaceLaneInt16x8(unsigned lane, Register rhs, FloatRegister lhsDest) { vpinsrw(lane, Operand(rhs), lhsDest, lhsDest); } +void MacroAssembler::replaceLaneInt32x4(unsigned lane, FloatRegister lhs, + Register rhs, FloatRegister dest) { + vpinsrd(lane, rhs, lhs, dest); +} + void MacroAssembler::replaceLaneInt32x4(unsigned lane, Register rhs, FloatRegister lhsDest) { vpinsrd(lane, rhs, lhsDest, lhsDest); } +void MacroAssembler::replaceLaneFloat32x4(unsigned lane, FloatRegister lhs, + FloatRegister rhs, + FloatRegister dest) { + MacroAssemblerX86Shared::replaceLaneFloat32x4(lane, lhs, rhs, dest); +} + void MacroAssembler::replaceLaneFloat32x4(unsigned lane, FloatRegister rhs, FloatRegister lhsDest) { - MacroAssemblerX86Shared::replaceLaneFloat32x4(rhs, lhsDest, lane); + MacroAssemblerX86Shared::replaceLaneFloat32x4(lane, lhsDest, rhs, lhsDest); +} + +void MacroAssembler::replaceLaneFloat64x2(unsigned lane, FloatRegister lhs, + FloatRegister rhs, + FloatRegister dest) { + MacroAssemblerX86Shared::replaceLaneFloat64x2(lane, lhs, rhs, dest); } void MacroAssembler::replaceLaneFloat64x2(unsigned lane, FloatRegister rhs, FloatRegister lhsDest) { - MacroAssemblerX86Shared::replaceLaneFloat64x2(rhs, lhsDest, lane); + MacroAssemblerX86Shared::replaceLaneFloat64x2(lane, lhsDest, rhs, lhsDest); } // Shuffle - permute with immediate indices @@ -1586,8 +1613,9 @@ void MacroAssembler::swizzleInt8x16Relaxed(FloatRegister rhs, // Integer Add -void MacroAssembler::addInt8x16(FloatRegister rhs, FloatRegister lhsDest) { - vpaddb(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::addInt8x16(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpaddb(Operand(rhs), lhs, dest); } void MacroAssembler::addInt8x16(FloatRegister lhs, const SimdConstant& rhs, @@ -1596,8 +1624,9 @@ void MacroAssembler::addInt8x16(FloatRegister lhs, const SimdConstant& rhs, &MacroAssembler::vpaddbSimd128); } -void MacroAssembler::addInt16x8(FloatRegister rhs, FloatRegister lhsDest) { - vpaddw(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::addInt16x8(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpaddw(Operand(rhs), lhs, dest); } void MacroAssembler::addInt16x8(FloatRegister lhs, const SimdConstant& rhs, @@ -1606,10 +1635,6 @@ void MacroAssembler::addInt16x8(FloatRegister lhs, const SimdConstant& rhs, &MacroAssembler::vpaddwSimd128); } -void MacroAssembler::addInt32x4(FloatRegister rhs, FloatRegister lhsDest) { - vpaddd(Operand(rhs), lhsDest, lhsDest); -} - void MacroAssembler::addInt32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { vpaddd(Operand(rhs), lhs, dest); @@ -1621,8 +1646,9 @@ void MacroAssembler::addInt32x4(FloatRegister lhs, const SimdConstant& rhs, &MacroAssembler::vpadddSimd128); } -void MacroAssembler::addInt64x2(FloatRegister rhs, FloatRegister lhsDest) { - vpaddq(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::addInt64x2(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpaddq(Operand(rhs), lhs, dest); } void MacroAssembler::addInt64x2(FloatRegister lhs, const SimdConstant& rhs, @@ -1633,8 +1659,9 @@ void MacroAssembler::addInt64x2(FloatRegister lhs, const SimdConstant& rhs, // Integer subtract -void MacroAssembler::subInt8x16(FloatRegister rhs, FloatRegister lhsDest) { - vpsubb(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::subInt8x16(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpsubb(Operand(rhs), lhs, dest); } void MacroAssembler::subInt8x16(FloatRegister lhs, const SimdConstant& rhs, @@ -1643,8 +1670,9 @@ void MacroAssembler::subInt8x16(FloatRegister lhs, const SimdConstant& rhs, &MacroAssembler::vpsubbSimd128); } -void MacroAssembler::subInt16x8(FloatRegister rhs, FloatRegister lhsDest) { - vpsubw(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::subInt16x8(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpsubw(Operand(rhs), lhs, dest); } void MacroAssembler::subInt16x8(FloatRegister lhs, const SimdConstant& rhs, @@ -1653,10 +1681,6 @@ void MacroAssembler::subInt16x8(FloatRegister lhs, const SimdConstant& rhs, &MacroAssembler::vpsubwSimd128); } -void MacroAssembler::subInt32x4(FloatRegister rhs, FloatRegister lhsDest) { - vpsubd(Operand(rhs), lhsDest, lhsDest); -} - void MacroAssembler::subInt32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { vpsubd(Operand(rhs), lhs, dest); @@ -1668,8 +1692,9 @@ void MacroAssembler::subInt32x4(FloatRegister lhs, const SimdConstant& rhs, &MacroAssembler::vpsubdSimd128); } -void MacroAssembler::subInt64x2(FloatRegister rhs, FloatRegister lhsDest) { - vpsubq(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::subInt64x2(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpsubq(Operand(rhs), lhs, dest); } void MacroAssembler::subInt64x2(FloatRegister lhs, const SimdConstant& rhs, @@ -1680,8 +1705,9 @@ void MacroAssembler::subInt64x2(FloatRegister lhs, const SimdConstant& rhs, // Integer multiply -void MacroAssembler::mulInt16x8(FloatRegister rhs, FloatRegister lhsDest) { - vpmullw(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::mulInt16x8(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpmullw(Operand(rhs), lhs, dest); } void MacroAssembler::mulInt16x8(FloatRegister lhs, const SimdConstant& rhs, @@ -1690,10 +1716,6 @@ void MacroAssembler::mulInt16x8(FloatRegister lhs, const SimdConstant& rhs, &MacroAssembler::vpmullwSimd128); } -void MacroAssembler::mulInt32x4(FloatRegister rhs, FloatRegister lhsDest) { - vpmulld(Operand(rhs), lhsDest, lhsDest); -} - void MacroAssembler::mulInt32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { vpmulld(Operand(rhs), lhs, dest); @@ -1707,7 +1729,6 @@ void MacroAssembler::mulInt32x4(FloatRegister lhs, const SimdConstant& rhs, void MacroAssembler::mulInt64x2(FloatRegister lhs, FloatRegister rhs, FloatRegister dest, FloatRegister temp) { - MOZ_ASSERT(lhs == dest); ScratchSimd128Scope temp2(*this); // lhs = // rhs = @@ -1723,7 +1744,7 @@ void MacroAssembler::mulInt64x2(FloatRegister lhs, FloatRegister rhs, vpaddq(Operand(temp), temp2, temp2); // temp2 = vpsllq(Imm32(32), temp2, temp2); // temp2 = <(DG+CH)_low 0> // <(BE+AF)_low 0> - vpmuludq(rhs, dest, dest); // dest = + vpmuludq(rhs, lhs, dest); // dest = // vpaddq(Operand(temp2), dest, dest); // dest = // <(DG+CH)_low+CG_high CG_low> @@ -1740,7 +1761,7 @@ void MacroAssembler::extMulLowInt8x16(FloatRegister rhs, ScratchSimd128Scope scratch(*this); widenLowInt8x16(rhs, scratch); widenLowInt8x16(lhsDest, lhsDest); - mulInt16x8(scratch, lhsDest); + mulInt16x8(lhsDest, scratch, lhsDest); } void MacroAssembler::extMulHighInt8x16(FloatRegister rhs, @@ -1748,7 +1769,7 @@ void MacroAssembler::extMulHighInt8x16(FloatRegister rhs, ScratchSimd128Scope scratch(*this); widenHighInt8x16(rhs, scratch); widenHighInt8x16(lhsDest, lhsDest); - mulInt16x8(scratch, lhsDest); + mulInt16x8(lhsDest, scratch, lhsDest); } void MacroAssembler::unsignedExtMulLowInt8x16(FloatRegister rhs, @@ -1756,7 +1777,7 @@ void MacroAssembler::unsignedExtMulLowInt8x16(FloatRegister rhs, ScratchSimd128Scope scratch(*this); unsignedWidenLowInt8x16(rhs, scratch); unsignedWidenLowInt8x16(lhsDest, lhsDest); - mulInt16x8(scratch, lhsDest); + mulInt16x8(lhsDest, scratch, lhsDest); } void MacroAssembler::unsignedExtMulHighInt8x16(FloatRegister rhs, @@ -1764,7 +1785,7 @@ void MacroAssembler::unsignedExtMulHighInt8x16(FloatRegister rhs, ScratchSimd128Scope scratch(*this); unsignedWidenHighInt8x16(rhs, scratch); unsignedWidenHighInt8x16(lhsDest, lhsDest); - mulInt16x8(scratch, lhsDest); + mulInt16x8(lhsDest, scratch, lhsDest); } void MacroAssembler::extMulLowInt16x8(FloatRegister rhs, @@ -1888,8 +1909,9 @@ void MacroAssembler::negInt64x2(FloatRegister src, FloatRegister dest) { // Saturating integer add -void MacroAssembler::addSatInt8x16(FloatRegister rhs, FloatRegister lhsDest) { - vpaddsb(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::addSatInt8x16(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpaddsb(Operand(rhs), lhs, dest); } void MacroAssembler::addSatInt8x16(FloatRegister lhs, const SimdConstant& rhs, @@ -1898,9 +1920,9 @@ void MacroAssembler::addSatInt8x16(FloatRegister lhs, const SimdConstant& rhs, &MacroAssembler::vpaddsbSimd128); } -void MacroAssembler::unsignedAddSatInt8x16(FloatRegister rhs, - FloatRegister lhsDest) { - vpaddusb(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::unsignedAddSatInt8x16(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpaddusb(Operand(rhs), lhs, dest); } void MacroAssembler::unsignedAddSatInt8x16(FloatRegister lhs, @@ -1910,8 +1932,9 @@ void MacroAssembler::unsignedAddSatInt8x16(FloatRegister lhs, &MacroAssembler::vpaddusbSimd128); } -void MacroAssembler::addSatInt16x8(FloatRegister rhs, FloatRegister lhsDest) { - vpaddsw(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::addSatInt16x8(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpaddsw(Operand(rhs), lhs, dest); } void MacroAssembler::addSatInt16x8(FloatRegister lhs, const SimdConstant& rhs, @@ -1920,9 +1943,9 @@ void MacroAssembler::addSatInt16x8(FloatRegister lhs, const SimdConstant& rhs, &MacroAssembler::vpaddswSimd128); } -void MacroAssembler::unsignedAddSatInt16x8(FloatRegister rhs, - FloatRegister lhsDest) { - vpaddusw(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::unsignedAddSatInt16x8(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpaddusw(Operand(rhs), lhs, dest); } void MacroAssembler::unsignedAddSatInt16x8(FloatRegister lhs, @@ -1934,8 +1957,9 @@ void MacroAssembler::unsignedAddSatInt16x8(FloatRegister lhs, // Saturating integer subtract -void MacroAssembler::subSatInt8x16(FloatRegister rhs, FloatRegister lhsDest) { - vpsubsb(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::subSatInt8x16(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpsubsb(Operand(rhs), lhs, dest); } void MacroAssembler::subSatInt8x16(FloatRegister lhs, const SimdConstant& rhs, @@ -1944,9 +1968,9 @@ void MacroAssembler::subSatInt8x16(FloatRegister lhs, const SimdConstant& rhs, &MacroAssembler::vpsubsbSimd128); } -void MacroAssembler::unsignedSubSatInt8x16(FloatRegister rhs, - FloatRegister lhsDest) { - vpsubusb(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::unsignedSubSatInt8x16(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpsubusb(Operand(rhs), lhs, dest); } void MacroAssembler::unsignedSubSatInt8x16(FloatRegister lhs, @@ -1956,8 +1980,9 @@ void MacroAssembler::unsignedSubSatInt8x16(FloatRegister lhs, &MacroAssembler::vpsubusbSimd128); } -void MacroAssembler::subSatInt16x8(FloatRegister rhs, FloatRegister lhsDest) { - vpsubsw(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::subSatInt16x8(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpsubsw(Operand(rhs), lhs, dest); } void MacroAssembler::subSatInt16x8(FloatRegister lhs, const SimdConstant& rhs, @@ -1966,9 +1991,9 @@ void MacroAssembler::subSatInt16x8(FloatRegister lhs, const SimdConstant& rhs, &MacroAssembler::vpsubswSimd128); } -void MacroAssembler::unsignedSubSatInt16x8(FloatRegister rhs, - FloatRegister lhsDest) { - vpsubusw(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::unsignedSubSatInt16x8(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpsubusw(Operand(rhs), lhs, dest); } void MacroAssembler::unsignedSubSatInt16x8(FloatRegister lhs, @@ -1980,8 +2005,9 @@ void MacroAssembler::unsignedSubSatInt16x8(FloatRegister lhs, // Lane-wise integer minimum -void MacroAssembler::minInt8x16(FloatRegister rhs, FloatRegister lhsDest) { - vpminsb(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::minInt8x16(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpminsb(Operand(rhs), lhs, dest); } void MacroAssembler::minInt8x16(FloatRegister lhs, const SimdConstant& rhs, @@ -1990,9 +2016,9 @@ void MacroAssembler::minInt8x16(FloatRegister lhs, const SimdConstant& rhs, &MacroAssembler::vpminsbSimd128); } -void MacroAssembler::unsignedMinInt8x16(FloatRegister rhs, - FloatRegister lhsDest) { - vpminub(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::unsignedMinInt8x16(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpminub(Operand(rhs), lhs, dest); } void MacroAssembler::unsignedMinInt8x16(FloatRegister lhs, @@ -2002,8 +2028,9 @@ void MacroAssembler::unsignedMinInt8x16(FloatRegister lhs, &MacroAssembler::vpminubSimd128); } -void MacroAssembler::minInt16x8(FloatRegister rhs, FloatRegister lhsDest) { - vpminsw(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::minInt16x8(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpminsw(Operand(rhs), lhs, dest); } void MacroAssembler::minInt16x8(FloatRegister lhs, const SimdConstant& rhs, @@ -2012,9 +2039,9 @@ void MacroAssembler::minInt16x8(FloatRegister lhs, const SimdConstant& rhs, &MacroAssembler::vpminswSimd128); } -void MacroAssembler::unsignedMinInt16x8(FloatRegister rhs, - FloatRegister lhsDest) { - vpminuw(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::unsignedMinInt16x8(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpminuw(Operand(rhs), lhs, dest); } void MacroAssembler::unsignedMinInt16x8(FloatRegister lhs, @@ -2024,8 +2051,9 @@ void MacroAssembler::unsignedMinInt16x8(FloatRegister lhs, &MacroAssembler::vpminuwSimd128); } -void MacroAssembler::minInt32x4(FloatRegister rhs, FloatRegister lhsDest) { - vpminsd(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::minInt32x4(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpminsd(Operand(rhs), lhs, dest); } void MacroAssembler::minInt32x4(FloatRegister lhs, const SimdConstant& rhs, @@ -2034,9 +2062,9 @@ void MacroAssembler::minInt32x4(FloatRegister lhs, const SimdConstant& rhs, &MacroAssembler::vpminsdSimd128); } -void MacroAssembler::unsignedMinInt32x4(FloatRegister rhs, - FloatRegister lhsDest) { - vpminud(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::unsignedMinInt32x4(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpminud(Operand(rhs), lhs, dest); } void MacroAssembler::unsignedMinInt32x4(FloatRegister lhs, @@ -2048,8 +2076,9 @@ void MacroAssembler::unsignedMinInt32x4(FloatRegister lhs, // Lane-wise integer maximum -void MacroAssembler::maxInt8x16(FloatRegister rhs, FloatRegister lhsDest) { - vpmaxsb(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::maxInt8x16(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpmaxsb(Operand(rhs), lhs, dest); } void MacroAssembler::maxInt8x16(FloatRegister lhs, const SimdConstant& rhs, @@ -2058,9 +2087,9 @@ void MacroAssembler::maxInt8x16(FloatRegister lhs, const SimdConstant& rhs, &MacroAssembler::vpmaxsbSimd128); } -void MacroAssembler::unsignedMaxInt8x16(FloatRegister rhs, - FloatRegister lhsDest) { - vpmaxub(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::unsignedMaxInt8x16(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpmaxub(Operand(rhs), lhs, dest); } void MacroAssembler::unsignedMaxInt8x16(FloatRegister lhs, @@ -2070,8 +2099,9 @@ void MacroAssembler::unsignedMaxInt8x16(FloatRegister lhs, &MacroAssembler::vpmaxubSimd128); } -void MacroAssembler::maxInt16x8(FloatRegister rhs, FloatRegister lhsDest) { - vpmaxsw(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::maxInt16x8(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpmaxsw(Operand(rhs), lhs, dest); } void MacroAssembler::maxInt16x8(FloatRegister lhs, const SimdConstant& rhs, @@ -2080,9 +2110,9 @@ void MacroAssembler::maxInt16x8(FloatRegister lhs, const SimdConstant& rhs, &MacroAssembler::vpmaxswSimd128); } -void MacroAssembler::unsignedMaxInt16x8(FloatRegister rhs, - FloatRegister lhsDest) { - vpmaxuw(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::unsignedMaxInt16x8(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpmaxuw(Operand(rhs), lhs, dest); } void MacroAssembler::unsignedMaxInt16x8(FloatRegister lhs, @@ -2092,8 +2122,9 @@ void MacroAssembler::unsignedMaxInt16x8(FloatRegister lhs, &MacroAssembler::vpmaxuwSimd128); } -void MacroAssembler::maxInt32x4(FloatRegister rhs, FloatRegister lhsDest) { - vpmaxsd(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::maxInt32x4(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpmaxsd(Operand(rhs), lhs, dest); } void MacroAssembler::maxInt32x4(FloatRegister lhs, const SimdConstant& rhs, @@ -2102,9 +2133,9 @@ void MacroAssembler::maxInt32x4(FloatRegister lhs, const SimdConstant& rhs, &MacroAssembler::vpmaxsdSimd128); } -void MacroAssembler::unsignedMaxInt32x4(FloatRegister rhs, - FloatRegister lhsDest) { - vpmaxud(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::unsignedMaxInt32x4(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpmaxud(Operand(rhs), lhs, dest); } void MacroAssembler::unsignedMaxInt32x4(FloatRegister lhs, @@ -2116,14 +2147,16 @@ void MacroAssembler::unsignedMaxInt32x4(FloatRegister lhs, // Lane-wise integer rounding average -void MacroAssembler::unsignedAverageInt8x16(FloatRegister rhs, - FloatRegister lhsDest) { - vpavgb(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::unsignedAverageInt8x16(FloatRegister lhs, + FloatRegister rhs, + FloatRegister dest) { + vpavgb(Operand(rhs), lhs, dest); } -void MacroAssembler::unsignedAverageInt16x8(FloatRegister rhs, - FloatRegister lhsDest) { - vpavgw(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::unsignedAverageInt16x8(FloatRegister lhs, + FloatRegister rhs, + FloatRegister dest) { + vpavgw(Operand(rhs), lhs, dest); } // Lane-wise integer absolute value @@ -2644,8 +2677,9 @@ void MacroAssembler::pseudoMaxFloat64x2(FloatRegister rhsOrRhsDest, // Widening/pairwise integer dot product -void MacroAssembler::widenDotInt16x8(FloatRegister rhs, FloatRegister lhsDest) { - vpmaddwd(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::widenDotInt16x8(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpmaddwd(Operand(rhs), lhs, dest); } void MacroAssembler::widenDotInt16x8(FloatRegister lhs, const SimdConstant& rhs, @@ -2690,10 +2724,6 @@ void MacroAssembler::nearestFloat64x2(FloatRegister src, FloatRegister dest) { // Floating add -void MacroAssembler::addFloat32x4(FloatRegister rhs, FloatRegister lhsDest) { - vaddps(Operand(rhs), lhsDest, lhsDest); -} - void MacroAssembler::addFloat32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { vaddps(Operand(rhs), lhs, dest); @@ -2705,8 +2735,9 @@ void MacroAssembler::addFloat32x4(FloatRegister lhs, const SimdConstant& rhs, &MacroAssembler::vaddpsSimd128); } -void MacroAssembler::addFloat64x2(FloatRegister rhs, FloatRegister lhsDest) { - vaddpd(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::addFloat64x2(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vaddpd(Operand(rhs), lhs, dest); } void MacroAssembler::addFloat64x2(FloatRegister lhs, const SimdConstant& rhs, @@ -2717,10 +2748,6 @@ void MacroAssembler::addFloat64x2(FloatRegister lhs, const SimdConstant& rhs, // Floating subtract -void MacroAssembler::subFloat32x4(FloatRegister rhs, FloatRegister lhsDest) { - vsubps(Operand(rhs), lhsDest, lhsDest); -} - void MacroAssembler::subFloat32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { vsubps(Operand(rhs), lhs, dest); @@ -2732,8 +2759,9 @@ void MacroAssembler::subFloat32x4(FloatRegister lhs, const SimdConstant& rhs, &MacroAssembler::vsubpsSimd128); } -void MacroAssembler::subFloat64x2(FloatRegister rhs, FloatRegister lhsDest) { - AssemblerX86Shared::vsubpd(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::subFloat64x2(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + AssemblerX86Shared::vsubpd(Operand(rhs), lhs, dest); } void MacroAssembler::subFloat64x2(FloatRegister lhs, const SimdConstant& rhs, @@ -2744,10 +2772,6 @@ void MacroAssembler::subFloat64x2(FloatRegister lhs, const SimdConstant& rhs, // Floating division -void MacroAssembler::divFloat32x4(FloatRegister rhs, FloatRegister lhsDest) { - vdivps(Operand(rhs), lhsDest, lhsDest); -} - void MacroAssembler::divFloat32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { vdivps(Operand(rhs), lhs, dest); @@ -2759,8 +2783,9 @@ void MacroAssembler::divFloat32x4(FloatRegister lhs, const SimdConstant& rhs, &MacroAssembler::vdivpsSimd128); } -void MacroAssembler::divFloat64x2(FloatRegister rhs, FloatRegister lhsDest) { - vdivpd(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::divFloat64x2(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vdivpd(Operand(rhs), lhs, dest); } void MacroAssembler::divFloat64x2(FloatRegister lhs, const SimdConstant& rhs, @@ -2771,10 +2796,6 @@ void MacroAssembler::divFloat64x2(FloatRegister lhs, const SimdConstant& rhs, // Floating Multiply -void MacroAssembler::mulFloat32x4(FloatRegister rhs, FloatRegister lhsDest) { - vmulps(Operand(rhs), lhsDest, lhsDest); -} - void MacroAssembler::mulFloat32x4(FloatRegister lhs, FloatRegister rhs, FloatRegister dest) { vmulps(Operand(rhs), lhs, dest); @@ -2786,8 +2807,9 @@ void MacroAssembler::mulFloat32x4(FloatRegister lhs, const SimdConstant& rhs, &MacroAssembler::vmulpsSimd128); } -void MacroAssembler::mulFloat64x2(FloatRegister rhs, FloatRegister lhsDest) { - vmulpd(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::mulFloat64x2(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vmulpd(Operand(rhs), lhs, dest); } void MacroAssembler::mulFloat64x2(FloatRegister lhs, const SimdConstant& rhs, @@ -2920,8 +2942,9 @@ void MacroAssembler::convertFloat32x4ToFloat64x2(FloatRegister src, // Integer to integer narrowing -void MacroAssembler::narrowInt16x8(FloatRegister rhs, FloatRegister lhsDest) { - vpacksswb(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::narrowInt16x8(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpacksswb(Operand(rhs), lhs, dest); } void MacroAssembler::narrowInt16x8(FloatRegister lhs, const SimdConstant& rhs, @@ -2930,9 +2953,9 @@ void MacroAssembler::narrowInt16x8(FloatRegister lhs, const SimdConstant& rhs, &MacroAssembler::vpacksswbSimd128); } -void MacroAssembler::unsignedNarrowInt16x8(FloatRegister rhs, - FloatRegister lhsDest) { - vpackuswb(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::unsignedNarrowInt16x8(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpackuswb(Operand(rhs), lhs, dest); } void MacroAssembler::unsignedNarrowInt16x8(FloatRegister lhs, @@ -2942,8 +2965,9 @@ void MacroAssembler::unsignedNarrowInt16x8(FloatRegister lhs, &MacroAssembler::vpackuswbSimd128); } -void MacroAssembler::narrowInt32x4(FloatRegister rhs, FloatRegister lhsDest) { - vpackssdw(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::narrowInt32x4(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpackssdw(Operand(rhs), lhs, dest); } void MacroAssembler::narrowInt32x4(FloatRegister lhs, const SimdConstant& rhs, @@ -2952,9 +2976,9 @@ void MacroAssembler::narrowInt32x4(FloatRegister lhs, const SimdConstant& rhs, &MacroAssembler::vpackssdwSimd128); } -void MacroAssembler::unsignedNarrowInt32x4(FloatRegister rhs, - FloatRegister lhsDest) { - vpackusdw(Operand(rhs), lhsDest, lhsDest); +void MacroAssembler::unsignedNarrowInt32x4(FloatRegister lhs, FloatRegister rhs, + FloatRegister dest) { + vpackusdw(Operand(rhs), lhs, dest); } void MacroAssembler::unsignedNarrowInt32x4(FloatRegister lhs, @@ -3037,34 +3061,34 @@ void MacroAssembler::unsignedWidenHighInt32x4(FloatRegister src, void MacroAssembler::fmaFloat32x4(FloatRegister src1, FloatRegister src2, FloatRegister srcDest) { - ScratchFloat32Scope scratch(*this); - moveSimd128(src1, scratch); - mulFloat32x4(src2, scratch); - addFloat32x4(scratch, srcDest); + ScratchSimd128Scope scratch(*this); + src1 = moveSimd128FloatIfNotAVX(src1, scratch); + mulFloat32x4(src1, src2, scratch); + addFloat32x4(srcDest, scratch, srcDest); } void MacroAssembler::fmsFloat32x4(FloatRegister src1, FloatRegister src2, FloatRegister srcDest) { - ScratchFloat32Scope scratch(*this); - moveSimd128(src1, scratch); - mulFloat32x4(src2, scratch); - subFloat32x4(scratch, srcDest); + ScratchSimd128Scope scratch(*this); + src1 = moveSimd128FloatIfNotAVX(src1, scratch); + mulFloat32x4(src1, src2, scratch); + subFloat32x4(srcDest, scratch, srcDest); } void MacroAssembler::fmaFloat64x2(FloatRegister src1, FloatRegister src2, FloatRegister srcDest) { - ScratchFloat32Scope scratch(*this); - moveSimd128(src1, scratch); - mulFloat64x2(src2, scratch); - addFloat64x2(scratch, srcDest); + ScratchSimd128Scope scratch(*this); + src1 = moveSimd128FloatIfNotAVX(src1, scratch); + mulFloat64x2(src1, src2, scratch); + addFloat64x2(srcDest, scratch, srcDest); } void MacroAssembler::fmsFloat64x2(FloatRegister src1, FloatRegister src2, FloatRegister srcDest) { - ScratchFloat32Scope scratch(*this); - moveSimd128(src1, scratch); - mulFloat64x2(src2, scratch); - subFloat64x2(scratch, srcDest); + ScratchSimd128Scope scratch(*this); + src1 = moveSimd128FloatIfNotAVX(src1, scratch); + mulFloat64x2(src1, src2, scratch); + subFloat64x2(srcDest, scratch, srcDest); } void MacroAssembler::minFloat32x4Relaxed(FloatRegister src, diff --git a/js/src/jit/x86-shared/MacroAssembler-x86-shared.h b/js/src/jit/x86-shared/MacroAssembler-x86-shared.h index a5d01f1a3db8..6b49dc898f3a 100644 --- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.h +++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.h @@ -447,10 +447,10 @@ class MacroAssemblerX86Shared : public Assembler { void extractLaneInt8x16(FloatRegister input, Register output, unsigned lane, SimdSign sign); - void replaceLaneFloat32x4(FloatRegister rhs, FloatRegister lhsDest, - unsigned lane); - void replaceLaneFloat64x2(FloatRegister rhs, FloatRegister lhsDest, - unsigned lane); + void replaceLaneFloat32x4(unsigned lane, FloatRegister lhs, FloatRegister rhs, + FloatRegister dest); + void replaceLaneFloat64x2(unsigned lane, FloatRegister lhs, FloatRegister rhs, + FloatRegister dest); void shuffleInt8x16(FloatRegister lhs, FloatRegister rhs, FloatRegister output, const uint8_t lanes[16]); diff --git a/js/src/jit/x86/MacroAssembler-x86-inl.h b/js/src/jit/x86/MacroAssembler-x86-inl.h index 93b57b74477b..ee301d726bad 100644 --- a/js/src/jit/x86/MacroAssembler-x86-inl.h +++ b/js/src/jit/x86/MacroAssembler-x86-inl.h @@ -1176,8 +1176,9 @@ void MacroAssembler::replaceLaneInt64x2(unsigned lane, FloatRegister lhs, } void MacroAssembler::splatX2(Register64 src, FloatRegister dest) { - replaceLaneInt64x2(0, src, dest); - replaceLaneInt64x2(1, src, dest); + vmovd(src.low, dest); + vpinsrd(1, src.high, dest, dest); + vpunpcklqdq(dest, dest, dest); } // ======================================================================== diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index acb381d7ae94..7ed2819c8cfa 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -265,6 +265,18 @@ JS_PUBLIC_API bool JS::ObjectOpResult::failNotDataDescriptor() { return fail(JSMSG_NOT_DATA_DESCRIPTOR); } +JS_PUBLIC_API bool JS::ObjectOpResult::failInvalidDescriptor() { + return fail(JSMSG_INVALID_DESCRIPTOR); +} + +JS_PUBLIC_API bool JS::ObjectOpResult::failBadArrayLength() { + return fail(JSMSG_BAD_ARRAY_LENGTH); +} + +JS_PUBLIC_API bool JS::ObjectOpResult::failBadIndex() { + return fail(JSMSG_BAD_INDEX); +} + JS_PUBLIC_API int64_t JS_Now() { return PRMJ_Now(); } JS_PUBLIC_API Value JS_GetEmptyStringValue(JSContext* cx) { diff --git a/js/src/wasm/WasmBaselineCompile.cpp b/js/src/wasm/WasmBaselineCompile.cpp index 860b3410cf3c..5af671c5eedd 100644 --- a/js/src/wasm/WasmBaselineCompile.cpp +++ b/js/src/wasm/WasmBaselineCompile.cpp @@ -6582,95 +6582,95 @@ static void XorV128(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { } static void AddI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.addInt8x16(rs, rsd); + masm.addInt8x16(rsd, rs, rsd); } static void AddI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.addInt16x8(rs, rsd); + masm.addInt16x8(rsd, rs, rsd); } static void AddI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.addInt32x4(rs, rsd); + masm.addInt32x4(rsd, rs, rsd); } static void AddF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.addFloat32x4(rs, rsd); + masm.addFloat32x4(rsd, rs, rsd); } static void AddI64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.addInt64x2(rs, rsd); + masm.addInt64x2(rsd, rs, rsd); } static void AddF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.addFloat64x2(rs, rsd); + masm.addFloat64x2(rsd, rs, rsd); } static void AddSatI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.addSatInt8x16(rs, rsd); + masm.addSatInt8x16(rsd, rs, rsd); } static void AddSatUI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.unsignedAddSatInt8x16(rs, rsd); + masm.unsignedAddSatInt8x16(rsd, rs, rsd); } static void AddSatI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.addSatInt16x8(rs, rsd); + masm.addSatInt16x8(rsd, rs, rsd); } static void AddSatUI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.unsignedAddSatInt16x8(rs, rsd); + masm.unsignedAddSatInt16x8(rsd, rs, rsd); } static void SubI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.subInt8x16(rs, rsd); + masm.subInt8x16(rsd, rs, rsd); } static void SubI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.subInt16x8(rs, rsd); + masm.subInt16x8(rsd, rs, rsd); } static void SubI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.subInt32x4(rs, rsd); + masm.subInt32x4(rsd, rs, rsd); } static void SubF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.subFloat32x4(rs, rsd); + masm.subFloat32x4(rsd, rs, rsd); } static void SubI64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.subInt64x2(rs, rsd); + masm.subInt64x2(rsd, rs, rsd); } static void SubF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.subFloat64x2(rs, rsd); + masm.subFloat64x2(rsd, rs, rsd); } static void SubSatI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.subSatInt8x16(rs, rsd); + masm.subSatInt8x16(rsd, rs, rsd); } static void SubSatUI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.unsignedSubSatInt8x16(rs, rsd); + masm.unsignedSubSatInt8x16(rsd, rs, rsd); } static void SubSatI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.subSatInt16x8(rs, rsd); + masm.subSatInt16x8(rsd, rs, rsd); } static void SubSatUI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.unsignedSubSatInt16x8(rs, rsd); + masm.unsignedSubSatInt16x8(rsd, rs, rsd); } static void MulI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.mulInt16x8(rs, rsd); + masm.mulInt16x8(rsd, rs, rsd); } static void MulI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.mulInt32x4(rs, rsd); + masm.mulInt32x4(rsd, rs, rsd); } static void MulF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.mulFloat32x4(rs, rsd); + masm.mulFloat32x4(rsd, rs, rsd); } # if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) @@ -6686,15 +6686,15 @@ static void MulI64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd, # endif static void MulF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.mulFloat64x2(rs, rsd); + masm.mulFloat64x2(rsd, rs, rsd); } static void DivF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.divFloat32x4(rs, rsd); + masm.divFloat32x4(rsd, rs, rsd); } static void DivF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.divFloat64x2(rs, rsd); + masm.divFloat64x2(rsd, rs, rsd); } # if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) @@ -6772,7 +6772,7 @@ static void PMaxF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { # endif static void DotI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.widenDotInt16x8(rs, rsd); + masm.widenDotInt16x8(rsd, rs, rsd); } static void ExtMulLowI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { @@ -7135,75 +7135,75 @@ static void ShiftRightUI64x2(MacroAssembler& masm, RegI32 rs, RegV128 rsd, # endif static void AverageUI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.unsignedAverageInt8x16(rs, rsd); + masm.unsignedAverageInt8x16(rsd, rs, rsd); } static void AverageUI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.unsignedAverageInt16x8(rs, rsd); + masm.unsignedAverageInt16x8(rsd, rs, rsd); } static void MinI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.minInt8x16(rs, rsd); + masm.minInt8x16(rsd, rs, rsd); } static void MinUI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.unsignedMinInt8x16(rs, rsd); + masm.unsignedMinInt8x16(rsd, rs, rsd); } static void MaxI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.maxInt8x16(rs, rsd); + masm.maxInt8x16(rsd, rs, rsd); } static void MaxUI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.unsignedMaxInt8x16(rs, rsd); + masm.unsignedMaxInt8x16(rsd, rs, rsd); } static void MinI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.minInt16x8(rs, rsd); + masm.minInt16x8(rsd, rs, rsd); } static void MinUI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.unsignedMinInt16x8(rs, rsd); + masm.unsignedMinInt16x8(rsd, rs, rsd); } static void MaxI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.maxInt16x8(rs, rsd); + masm.maxInt16x8(rsd, rs, rsd); } static void MaxUI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.unsignedMaxInt16x8(rs, rsd); + masm.unsignedMaxInt16x8(rsd, rs, rsd); } static void MinI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.minInt32x4(rs, rsd); + masm.minInt32x4(rsd, rs, rsd); } static void MinUI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.unsignedMinInt32x4(rs, rsd); + masm.unsignedMinInt32x4(rsd, rs, rsd); } static void MaxI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.maxInt32x4(rs, rsd); + masm.maxInt32x4(rsd, rs, rsd); } static void MaxUI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.unsignedMaxInt32x4(rs, rsd); + masm.unsignedMaxInt32x4(rsd, rs, rsd); } static void NarrowI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.narrowInt16x8(rs, rsd); + masm.narrowInt16x8(rsd, rs, rsd); } static void NarrowUI16x8(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.unsignedNarrowInt16x8(rs, rsd); + masm.unsignedNarrowInt16x8(rsd, rs, rsd); } static void NarrowI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.narrowInt32x4(rs, rsd); + masm.narrowInt32x4(rsd, rs, rsd); } static void NarrowUI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rsd) { - masm.unsignedNarrowInt32x4(rs, rsd); + masm.unsignedNarrowInt32x4(rsd, rs, rsd); } static void WidenLowI8x16(MacroAssembler& masm, RegV128 rs, RegV128 rd) { diff --git a/js/xpconnect/tests/marionette/test_preloader_telemetry.py b/js/xpconnect/tests/marionette/test_preloader_telemetry.py index dc1f322fda40..7f079687534a 100644 --- a/js/xpconnect/tests/marionette/test_preloader_telemetry.py +++ b/js/xpconnect/tests/marionette/test_preloader_telemetry.py @@ -5,8 +5,10 @@ from __future__ import absolute_import import time import os + from marionette_harness import MarionetteTestCase + LABELS_SCRIPT_PRELOADER_REQUESTS = { "Hit": "0", "HitChild": "1", @@ -18,11 +20,14 @@ class TestScriptPreloader(MarionetteTestCase): def test_preloader_requests_histogram(self): start_time = time.time() self.invalidate_caches() + self.marionette.restart(clean=False, in_app=True) + histogram = self.get_histogram("SCRIPT_PRELOADER_REQUESTS") misses = histogram.get(LABELS_SCRIPT_PRELOADER_REQUESTS["Miss"], 0) hits = histogram.get(LABELS_SCRIPT_PRELOADER_REQUESTS["Hit"], 0) child_hits = histogram.get(LABELS_SCRIPT_PRELOADER_REQUESTS["HitChild"], 0) + self.assertMuchGreaterThan(misses, hits) self.assertMuchGreaterThan(misses, child_hits) @@ -34,21 +39,23 @@ class TestScriptPreloader(MarionetteTestCase): start_time, "{}/startupCache/scriptCache-child.bin".format(profile) ) self.marionette.restart(clean=False, in_app=True) + histogram = self.get_histogram("SCRIPT_PRELOADER_REQUESTS") misses = histogram.get(LABELS_SCRIPT_PRELOADER_REQUESTS["Miss"], 0) hits = histogram.get(LABELS_SCRIPT_PRELOADER_REQUESTS["Hit"], 0) child_hits = histogram.get(LABELS_SCRIPT_PRELOADER_REQUESTS["HitChild"], 0) + # This is just heuristic. We certainly want to be made aware if this ratio # changes and we didn't intend it to. self.assertSimilar(misses, hits) self.assertNotEqual(child_hits, 0) def assertMuchGreaterThan(self, lhs, rhs): - self.assertTrue(lhs > 4 * rhs) + self.assertGreater(lhs, 4 * rhs) def assertSimilar(self, lhs, rhs): - self.assertTrue(lhs < 2 * rhs) - self.assertTrue(lhs > rhs / 2) + self.assertLess(lhs, 2 * rhs) + self.assertGreater(lhs, rhs / 2) def wait_for_file_change(self, start_time, path): expires = time.time() + 20 @@ -64,24 +71,23 @@ class TestScriptPreloader(MarionetteTestCase): def wait_for_observer_notification(self, name): with self.marionette.using_context(self.marionette.CONTEXT_CHROME): - return self.marionette.execute_script( + return self.marionette.execute_async_script( """ - let [resolve] = arguments; - Services.obs.addObserver(() => resolve(), "{}"); - """.format( - name - ) + const [name, resolve] = arguments; + Services.obs.addObserver(() => resolve(), name); + """, + script_args=(name,), ) def get_histogram(self, name): with self.marionette.using_context(self.marionette.CONTEXT_CHROME): return self.marionette.execute_script( """ - let snapshot = Services.telemetry.getSnapshotForHistograms("main", true); - return snapshot.parent.{}.values; - """.format( - name - ) + const name = arguments[0]; + const snapshot = Services.telemetry.getSnapshotForHistograms("main", true); + return snapshot.parent[name].values; + """, + script_args=(name,), ) def invalidate_caches(self): diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp index 5822323083c4..6a07f3a3076d 100644 --- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -33,6 +33,7 @@ #include "mozilla/FloatingPoint.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/BrowsingContext.h" +#include "mozilla/dom/ProxyHandlerUtils.h" #include "mozilla/dom/WindowBinding.h" #include "mozilla/dom/WindowProxyHolder.h" #include "mozilla/dom/XrayExpandoClass.h" diff --git a/layout/generic/nsFrameSelection.cpp b/layout/generic/nsFrameSelection.cpp index 848e227adda5..330264385ff2 100644 --- a/layout/generic/nsFrameSelection.cpp +++ b/layout/generic/nsFrameSelection.cpp @@ -1926,7 +1926,8 @@ nsresult nsFrameSelection::PageMove(bool aForward, bool aExtend, { // We don't want any script to run until we check whether selection is // modified by HandleClick. - SelectionBatcher ensureNoSelectionChangeNotifications(selection); + SelectionBatcher ensureNoSelectionChangeNotifications(selection, + __FUNCTION__); RangeBoundary oldAnchor = selection->AnchorRef(); RangeBoundary oldFocus = selection->FocusRef(); @@ -2148,9 +2149,22 @@ nsFrameSelection::CreateRangeExtendedToSomewhere( //////////END FRAMESELECTION -void nsFrameSelection::StartBatchChanges() { mBatching.mCounter++; } +LazyLogModule gBatchLog("SelectionBatch"); -void nsFrameSelection::EndBatchChanges(int16_t aReasons) { +void nsFrameSelection::StartBatchChanges(const char* aRequesterFuncName) { + MOZ_LOG(gBatchLog, LogLevel::Info, + ("%p%snsFrameSelection::StartBatchChanges(%s)", this, + std::string((mBatching.mCounter + 1) * 2, ' ').c_str(), + aRequesterFuncName)); + mBatching.mCounter++; +} + +void nsFrameSelection::EndBatchChanges(const char* aRequesterFuncName, + int16_t aReasons) { + MOZ_LOG(gBatchLog, LogLevel::Info, + ("%p%snsFrameSelection::EndBatchChanges (%s, %s)", this, + std::string(mBatching.mCounter * 2, ' ').c_str(), aRequesterFuncName, + SelectionChangeReasonsToCString(aReasons).get())); MOZ_ASSERT(mBatching.mCounter > 0, "Bad mBatching.mCounter"); mBatching.mCounter--; @@ -2254,7 +2268,7 @@ nsresult nsFrameSelection::TableSelection::HandleSelection( // Stack-class to wrap all table selection changes in // BeginBatchChanges() / EndBatchChanges() - SelectionBatcher selectionBatcher(&aNormalSelection); + SelectionBatcher selectionBatcher(&aNormalSelection, __FUNCTION__); if (aDragState && mDragSelectingCells) { return HandleDragSelecting(aTarget, childContent, aMouseEvent, diff --git a/layout/generic/nsFrameSelection.h b/layout/generic/nsFrameSelection.h index 749807236bd2..28d9af6fd8e0 100644 --- a/layout/generic/nsFrameSelection.h +++ b/layout/generic/nsFrameSelection.h @@ -728,14 +728,23 @@ class nsFrameSelection final { nsFrameSelection(mozilla::PresShell* aPresShell, nsIContent* aLimiter, bool aAccessibleCaretEnabled); - void StartBatchChanges(); - - MOZ_CAN_RUN_SCRIPT_BOUNDARY /** + * @param aRequesterFuncName function name which wants to start the batch. + * This won't be stored nor exposed to selection listeners etc, used only for + * logging. + */ + void StartBatchChanges(const char* aRequesterFuncName); + + /** + * @param aRequesterFuncName function name which wants to end the batch. + * This won't be stored nor exposed to selection listeners etc, used only for + * logging. * @param aReasons potentially multiple of the reasons defined in * nsISelectionListener.idl */ - void EndBatchChanges(int16_t aReasons = nsISelectionListener::NO_REASON); + MOZ_CAN_RUN_SCRIPT_BOUNDARY void EndBatchChanges( + const char* aRequesterFuncName, + int16_t aReasons = nsISelectionListener::NO_REASON); mozilla::PresShell* GetPresShell() const { return mPresShell; } diff --git a/layout/reftests/bugs/459443-1.html b/layout/reftests/bugs/459443-1.html index 1e8d9d51e66f..c2d641229676 100644 --- a/layout/reftests/bugs/459443-1.html +++ b/layout/reftests/bugs/459443-1.html @@ -4,17 +4,31 @@