Merge autoland to mozilla-central. a=merge

This commit is contained in:
Butkovits Atila 2022-03-11 11:36:15 +02:00
commit c3894603d8
192 changed files with 8067 additions and 2538 deletions

View file

@ -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)
);
},

View file

@ -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.

View file

@ -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",

View file

@ -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;
}

View file

@ -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";

View file

@ -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 (
<div className="stp_tag_picker">
<p>Add Tags:</p>
<div className="stp_tag_picker_tags">
{tags.map((tag, i) => (
<div
className={`stp_tag_picker_tag${
duplicateTag === tag ? ` stp_tag_picker_tag_duplicate` : ``
}`}
>
{tag}
<button
onClick={() => removeTag(i)}
className={`stp_tag_picker_tag_remove`}
>
X
</button>
</div>
))}
<input
className="stp_tag_picker_input"
type="text"
onKeyDown={e => handleKeyDown(e)}
maxlength="25"
></input>
</div>
</div>
);
}
export default TagPicker;

View file

@ -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;
}
}

View file

@ -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__);
/******/
/******/ })()

View file

@ -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 = {
},
]}
/>
<h4 className="stp_styleguide_h4">TagPicker</h4>
<TagPicker tags={[`futurism`, `politics`, `mozilla`]} />
<h3>Typography:</h3>
<h2 className="header_large">.header_large</h2>
<h3 className="header_medium">.header_medium</h3>

View file

@ -9,7 +9,7 @@
<title>Pocket: Style Guide</title>
<link rel="stylesheet" href="css/main.compiled.css">
</head>
<body class="pkt_ext_container_style_guide">
<body>
<script src="js/vendor/jquery-2.1.1.min.js"></script>
<script src="js/vendor/handlebars.runtime.js"></script>
<script src="js/tmpl.js"></script>

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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})`;
}

View file

@ -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",

View file

@ -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",

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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<String> 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<String> 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,

View file

@ -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<json> 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);
},
});
/**

View file

@ -213,7 +213,7 @@ const ContentProcessTargetActor = TargetActorMixin(
if (this.isDestroyed()) {
return;
}
Resources.unwatchAllTargetResources(this);
Resources.unwatchAllResources(this);
Actor.prototype.destroy.call(this);

View file

@ -727,7 +727,7 @@ const windowGlobalTargetPrototype = {
Actor.prototype.destroy.call(this);
TargetActorRegistry.unregisterTargetActor(this);
Resources.unwatchAllTargetResources(this);
Resources.unwatchAllResources(this);
},
/**

View file

@ -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:

View file

@ -6,6 +6,7 @@ DIRS += [
"inspected-window",
"inspector",
"resource",
"root-resource",
"script",
"target",
"target-configuration",

View file

@ -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",
)

View file

@ -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;

View file

@ -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"),
},
},
};

View file

@ -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"

View file

@ -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<nsFrameSelection> 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<nsFrameSelection> 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<int32_t> 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 ||

View file

@ -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<Selection> mSelection;
int16_t mReason;
const RefPtr<Selection> 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);
}
}
};

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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
%}

View file

@ -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<JSObject*> src,
return NS_OK;
}
template <decltype(JS::NewMapObject) Method>
bool GetMaplikeSetlikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
size_t aSlotIndex,
JS::MutableHandle<JSObject*> aBackingObj,
bool* aBackingObjCreated) {
template <auto Method, typename... Args>
static bool GetBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
size_t aSlotIndex,
JS::MutableHandle<JSObject*> aBackingObj,
bool* aBackingObjCreated, Args... aArgs) {
JS::Rooted<JSObject*> reflector(aCx);
reflector = IsDOMObject(aObj)
? aObj
@ -3526,7 +3527,7 @@ bool GetMaplikeSetlikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
{
JSAutoRealm ar(aCx, reflector);
JS::Rooted<JSObject*> 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<JSObject*> aObj,
size_t aSlotIndex,
JS::MutableHandle<JSObject*> aBackingObj,
bool* aBackingObjCreated) {
return GetMaplikeSetlikeBackingObject<JS::NewMapObject>(
aCx, aObj, aSlotIndex, aBackingObj, aBackingObjCreated);
return GetBackingObject<JS::NewMapObject>(aCx, aObj, aSlotIndex, aBackingObj,
aBackingObjCreated);
}
bool GetSetlikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
size_t aSlotIndex,
JS::MutableHandle<JSObject*> aBackingObj,
bool* aBackingObjCreated) {
return GetMaplikeSetlikeBackingObject<JS::NewSetObject>(
aCx, aObj, aSlotIndex, aBackingObj, aBackingObjCreated);
return GetBackingObject<JS::NewSetObject>(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<JSObject*> aObj, size_t aSlotIndex,
JS::MutableHandle<JSObject*> aBackingObj, bool* aBackingObjCreated,
const ObservableArrayProxyHandler* aHandler) {
return GetBackingObject<NewObservableArrayProxyObject>(
aCx, aObj, aSlotIndex, aBackingObj, aBackingObjCreated, aHandler);
}
bool ForEachHandler(JSContext* aCx, unsigned aArgc, JS::Value* aVp) {

View file

@ -62,6 +62,7 @@ class CustomElementReactionsStack;
class Document;
class EventTarget;
class MessageManagerGlobal;
class ObservableArrayProxyHandler;
class DedicatedWorkerGlobalScope;
template <typename KeyType, typename ValueType>
class Record;
@ -370,6 +371,13 @@ MOZ_ALWAYS_INLINE nsresult UnwrapObject(JSObject* obj, OwningNonNull<U>& value,
obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, cx);
}
template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
MOZ_ALWAYS_INLINE nsresult UnwrapObject(JSObject* obj, NonNull<U>& value,
const CxType& cx) {
return binding_detail::UnwrapObjectInternal<T, true>(
obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, cx);
}
// An UnwrapObject overload that just calls one of the JSObject* ones.
template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
MOZ_ALWAYS_INLINE nsresult UnwrapObject(JS::Handle<JS::Value> obj, U& value,
@ -378,6 +386,13 @@ MOZ_ALWAYS_INLINE nsresult UnwrapObject(JS::Handle<JS::Value> obj, U& value,
return UnwrapObject<PrototypeID, T>(&obj.toObject(), value, cx);
}
template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
MOZ_ALWAYS_INLINE nsresult UnwrapObject(JS::Handle<JS::Value> obj,
NonNull<U>& value, const CxType& cx) {
MOZ_ASSERT(obj.isObject());
return UnwrapObject<PrototypeID, T>(&obj.toObject(), value, cx);
}
template <prototypes::ID PrototypeID>
MOZ_ALWAYS_INLINE void AssertStaticUnwrapOK() {
static_assert(PrototypeID != prototypes::id::Window,
@ -3104,6 +3119,16 @@ bool GetSetlikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
JS::MutableHandle<JSObject*> 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<JSObject*> aObj, size_t aSlotIndex,
JS::MutableHandle<JSObject*> 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

File diff suppressed because it is too large Load diff

View file

@ -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<const DOMProxyHandler*>(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<jsid> 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

View file

@ -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 */

View file

@ -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<JS::PropertyDescriptor> 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<Maybe<JS::PropertyDescriptor>> 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<jsid> 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

View file

@ -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<JS::PropertyDescriptor> 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<Maybe<JS::PropertyDescriptor>> 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<jsid> 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<const ObservableArrayProxyHandler*>(
js::GetProxyHandler(obj));
}
} // namespace dom
} // namespace mozilla
#endif /* mozilla_dom_ObservableArrayProxyHandler_h */

View file

@ -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<jsid> 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 */

View file

@ -275,6 +275,12 @@ template <typename T>
return ToJSValue(aCx, *aArgument.get(), aValue);
}
template <typename T>
[[nodiscard]] bool ToJSValue(JSContext* aCx, const OwningNonNull<T>& aArgument,
JS::MutableHandle<JS::Value> aValue) {
return ToJSValue(aCx, *aArgument.get(), aValue);
}
// Accept WebIDL dictionaries
template <class T>
[[nodiscard]] std::enable_if_t<std::is_base_of<DictionaryBase, T>::value, bool>

View file

@ -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"

View file

@ -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",

View file

@ -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

View file

@ -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")

View file

@ -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<void> foo;
};
""",
"use void as inner type",
)
# Test dictionary as inner type
harness.should_throw(
parser,
"""
dictionary A {
boolean member;
};
interface B {
attribute ObservableArray<A> foo;
};
""",
"use dictionary as inner type",
)
# Test sequence as inner type
harness.should_throw(
parser,
"""
interface A {
attribute ObservableArray<sequence<boolean>> foo;
};
""",
"use sequence as inner type",
)
# Test sequence<dictionary> as inner type
harness.should_throw(
parser,
"""
dictionary A {
boolean member;
};
interface B {
attribute ObservableArray<sequence<A>> foo;
};
""",
"use sequence<dictionary> as inner type",
)
# Test record as inner type
harness.should_throw(
parser,
"""
interface A {
attribute ObservableArray<record<DOMString, boolean>> foo;
};
""",
"use record as inner type",
)
# Test record<dictionary> as inner type
harness.should_throw(
parser,
"""
dictionary A {
boolean member;
};
interface B {
attribute ObservableArray<record<DOMString, A>> foo;
};
""",
"use record<dictionary> as inner type",
)
# Test observable array as inner type
harness.should_throw(
parser,
"""
interface A {
attribute ObservableArray<ObservableArray<boolean>> foo;
};
""",
"use ObservableArray as inner type",
)
# Test nullable attribute
harness.should_throw(
parser,
"""
interface A {
attribute ObservableArray<boolean>? foo;
};
""",
"nullable",
)
# Test sequence
harness.should_throw(
parser,
"""
interface A {
void foo(sequence<ObservableArray<boolean>> foo);
};
""",
"used in sequence",
)
# Test record
harness.should_throw(
parser,
"""
interface A {
void foo(record<DOMString, ObservableArray<boolean>> foo);
};
""",
"used in record",
)
# Test promise
harness.should_throw(
parser,
"""
interface A {
Promise<ObservableArray<boolean>> foo();
};
""",
"used in promise",
)
# Test union
harness.should_throw(
parser,
"""
interface A {
attribute (DOMString or ObservableArray<boolean>>) foo;
};
""",
"used in union",
)
# Test dictionary member
harness.should_throw(
parser,
"""
dictionary A {
ObservableArray<boolean> foo;
};
""",
"used on dictionary member type",
)
# Test argument
harness.should_throw(
parser,
"""
interface A {
void foo(ObservableArray<boolean> foo);
};
""",
"used on argument",
)
# Test static attribute
harness.should_throw(
parser,
"""
interface A {
static attribute ObservableArray<boolean> foo;
};
""",
"used on static attribute type",
)
# Test iterable
harness.should_throw(
parser,
"""
interface A {
iterable<ObservableArray<boolean>>;
};
""",
"used in iterable",
)
# Test maplike
harness.should_throw(
parser,
"""
interface A {
maplike<long, ObservableArray<boolean>>;
};
""",
"used in maplike",
)
# Test setlike
harness.should_throw(
parser,
"""
interface A {
setlike<ObservableArray<boolean>>;
};
""",
"used in setlike",
)
# Test JS implemented interface
harness.should_throw(
parser,
"""
[JSImplementation="@mozilla.org/dom/test-interface-js;1"]
interface A {
readonly attribute ObservableArray<boolean> foo;
};
""",
"used in JS implemented interface",
)
# Test namespace
harness.should_throw(
parser,
"""
namespace A {
readonly attribute ObservableArray<boolean> foo;
};
""",
"used in namespaces",
)
# Test [Cached] extended attribute
harness.should_throw(
parser,
"""
interface A {
[Cached, Pure]
readonly attribute ObservableArray<boolean> foo;
};
""",
"have Cached extended attribute",
)
# Test [StoreInSlot] extended attribute
harness.should_throw(
parser,
"""
interface A {
[StoreInSlot, Pure]
readonly attribute ObservableArray<boolean> foo;
};
""",
"have StoreInSlot extended attribute",
)
# Test regular attribute
parser = parser.reset()
parser.parse(
"""
interface A {
readonly attribute ObservableArray<boolean> foo;
attribute ObservableArray<[Clamp] octet> bar;
attribute ObservableArray<long?> 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")

View file

@ -848,6 +848,24 @@ class TestInterface : public nsISupports, public nsWrapperCache {
Promise* ReceivePromise();
already_AddRefed<Promise> ReceiveAddrefedPromise();
// ObservableArray types
void OnDeleteBooleanObservableArray(bool, uint32_t, ErrorResult&);
void OnSetBooleanObservableArray(bool, uint32_t, ErrorResult&);
void OnDeleteObjectObservableArray(JSContext*, JS::Handle<JSObject*>,
uint32_t, ErrorResult&);
void OnSetObjectObservableArray(JSContext*, JS::Handle<JSObject*>, uint32_t,
ErrorResult&);
void OnDeleteAnyObservableArray(JSContext*, JS::Handle<JS::Value>, uint32_t,
ErrorResult&);
void OnSetAnyObservableArray(JSContext*, JS::Handle<JS::Value>, uint32_t,
ErrorResult&);
void OnDeleteInterfaceObservableArray(TestInterface*, uint32_t, ErrorResult&);
void OnSetInterfaceObservableArray(TestInterface*, uint32_t, ErrorResult&);
void OnDeleteNullableObservableArray(const Nullable<int>&, uint32_t,
ErrorResult&);
void OnSetNullableObservableArray(const Nullable<int>&, uint32_t,
ErrorResult&);
// binaryNames tests
void MethodRenamedTo();
void MethodRenamedTo(int8_t);

View file

@ -790,6 +790,13 @@ interface TestInterface {
Promise<any> receivePromise();
Promise<any> receiveAddrefedPromise();
// ObservableArray types
attribute ObservableArray<boolean> booleanObservableArray;
attribute ObservableArray<object> objectObservableArray;
attribute ObservableArray<any> anyObservableArray;
attribute ObservableArray<TestInterface> interfaceObservableArray;
attribute ObservableArray<long?> nullableObservableArray;
// binaryNames tests
[BinaryName="methodRenamedTo"]
void methodRenamedFrom();

View file

@ -591,6 +591,13 @@ interface TestExampleInterface {
Promise<any> receivePromise();
Promise<any> receiveAddrefedPromise();
// ObservableArray types
attribute ObservableArray<boolean> booleanObservableArray;
attribute ObservableArray<object> objectObservableArray;
attribute ObservableArray<any> anyObservableArray;
attribute ObservableArray<TestInterface> interfaceObservableArray;
attribute ObservableArray<long?> nullableObservableArray;
// binaryNames tests
[BinaryName="methodRenamedTo"]
void methodRenamedFrom();

View file

@ -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>
TestInterfaceObservableArray::Constructor(
const GlobalObject& aGlobal, const ObservableArrayCallbacks& aCallbacks,
ErrorResult& aRv) {
nsCOMPtr<nsPIDOMWindowInner> window =
do_QueryInterface(aGlobal.GetAsSupports());
if (!window) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
RefPtr<TestInterfaceObservableArray> r =
new TestInterfaceObservableArray(window, aCallbacks);
return r.forget();
}
JSObject* TestInterfaceObservableArray::WrapObject(
JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
return TestInterfaceObservableArray_Binding::Wrap(aCx, this, aGivenProto);
}
nsPIDOMWindowInner* TestInterfaceObservableArray::GetParentObject() const {
return mParent;
}
void TestInterfaceObservableArray::OnSetObservableArrayObject(
JSContext* aCx, JS::Handle<JSObject*> 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<JSObject*> 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<JSObject*> aValue,
ErrorResult& aRv) {
TestInterfaceObservableArray_Binding::ObservableArrayObjectHelpers::ElementAt(
this, aCx, aIndex, aValue, aRv);
}
already_AddRefed<TestInterfaceObservableArray>
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<JSObject*> 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<JSObject*> 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

View file

@ -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<JSObject*> aGivenProto) override;
static already_AddRefed<TestInterfaceObservableArray> Constructor(
const GlobalObject& aGlobal, const ObservableArrayCallbacks& aCallbacks,
ErrorResult& rv);
MOZ_CAN_RUN_SCRIPT_BOUNDARY
void OnSetObservableArrayObject(JSContext* aCx, JS::Handle<JSObject*> aValue,
uint32_t aIndex, ErrorResult& aRv);
MOZ_CAN_RUN_SCRIPT_BOUNDARY
void OnDeleteObservableArrayObject(JSContext* aCx,
JS::Handle<JSObject*> 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<JSObject*> aValue,
ErrorResult& aRv);
already_AddRefed<TestInterfaceObservableArray> InterfaceElementAtInternal(
uint32_t aIndex, ErrorResult& aRv);
void BooleanReplaceElementAtInternal(uint32_t aIndex, bool aValue,
ErrorResult& aRv);
void ObjectReplaceElementAtInternal(JSContext* aCx, uint32_t aIndex,
JS::Handle<JSObject*> aValue,
ErrorResult& aRv);
void InterfaceReplaceElementAtInternal(uint32_t aIndex,
TestInterfaceObservableArray& aValue,
ErrorResult& aRv);
void BooleanAppendElementInternal(bool aValue, ErrorResult& aRv);
void ObjectAppendElementInternal(JSContext* aCx, JS::Handle<JSObject*> 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<nsPIDOMWindowInner> mParent;
RefPtr<SetDeleteBooleanCallback> mSetBooleanCallback;
RefPtr<SetDeleteBooleanCallback> mDeleteBooleanCallback;
RefPtr<SetDeleteObjectCallback> mSetObjectCallback;
RefPtr<SetDeleteObjectCallback> mDeleteObjectCallback;
RefPtr<SetDeleteInterfaceCallback> mSetInterfaceCallback;
RefPtr<SetDeleteInterfaceCallback> mDeleteInterfaceCallback;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_TestInterfaceObservableArray_h

View file

@ -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]

View file

@ -0,0 +1,523 @@
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<!DOCTYPE HTML>
<html>
<head>
<title>Test Observable Array Type</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<script>
/* global TestInterfaceObservableArray */
add_task(async function init() {
await SpecialPowers.pushPrefEnv({set: [["dom.expose_test_interfaces", true]]});
});
add_task(function testObservableArray_length() {
let setCallbackCount = 0;
let deleteCallbackCount = 0;
let deleteCallbackTests = null;
let m = new TestInterfaceObservableArray({
setBooleanCallback(value, index) {
setCallbackCount++;
},
deleteBooleanCallback(value, index) {
deleteCallbackCount++;
if (typeof deleteCallbackTests === 'function') {
deleteCallbackTests(value, index);
}
},
});
m.observableArrayBoolean = [true, true, true, true, true];
let b = m.observableArrayBoolean;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 5, "length of observable array should be 5");
[
// [length, shouldThrow, expectedResult]
["invalid", true, false],
[b.length + 1, false, false],
[b.length, false, true],
[b.length - 1, false, true],
[0, false, true],
].forEach(function([length, shouldThrow, expectedResult]) {
// Initialize
let oldValues = b.slice();
let oldLen = b.length;
let shouldSuccess = !shouldThrow && expectedResult;
setCallbackCount = 0;
deleteCallbackCount = 0;
deleteCallbackTests = null;
if (shouldSuccess) {
let deleteCallbackIndex = b.length - 1;
deleteCallbackTests = function(_value, _index) {
info(`delete callback for ${_index}`);
is(_value, oldValues[deleteCallbackIndex], "deleteCallbackTests: test value argument");
is(_index, deleteCallbackIndex, "deleteCallbackTests: test index argument");
deleteCallbackIndex--;
};
}
// Test
info(`setting length to ${length}`);
try {
b.length = length;
ok(!shouldThrow, `setting length should throw`);
} catch(e) {
ok(shouldThrow, `setting length throws ${e}`);
}
is(setCallbackCount, 0, "setCallback should not be called");
is(deleteCallbackCount, shouldSuccess ? (oldLen - length) : 0, "deleteCallback count");
isDeeply(b, shouldSuccess ? oldValues.slice(0, length) : oldValues, "property values");
is(b.length, shouldSuccess ? length : oldLen, `length of observable array`);
});
});
add_task(function testObservableArray_length_callback_throw() {
let setCallbackCount = 0;
let deleteCallbackCount = 0;
let m = new TestInterfaceObservableArray({
setBooleanCallback(value, index) {
setCallbackCount++;
},
deleteBooleanCallback(value, index) {
deleteCallbackCount++;
if (value) {
throw new Error("deleteBooleanCallback");
}
},
});
m.observableArrayBoolean = [true, true, false, false, false];
let b = m.observableArrayBoolean;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 5, "length of observable array should be 5");
// Initialize
setCallbackCount = 0;
deleteCallbackCount = 0;
// Test
info(`setting length to 0`);
try {
b.length = 0;
ok(false, `setting length should throw`);
} catch(e) {
ok(true, `setting length throws ${e}`);
}
is(setCallbackCount, 0, "setCallback should not be called");
is(deleteCallbackCount, 4, "deleteCallback should be called");
isDeeply(b, [true, true], "property values");
is(b.length, 2, `length of observable array`);
});
add_task(function testObservableArray_setter() {
let setCallbackCount = 0;
let deleteCallbackCount = 0;
let setCallbackTests = null;
let deleteCallbackTests = null;
let m = new TestInterfaceObservableArray({
setBooleanCallback(value, index) {
setCallbackCount++;
if (typeof setCallbackTests === 'function') {
setCallbackTests(value, index);
}
},
deleteBooleanCallback(value, index) {
deleteCallbackCount++;
if (typeof deleteCallbackTests === 'function') {
deleteCallbackTests(value, index);
}
},
});
let b = m.observableArrayBoolean;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 0, "length of observable array should be 0");
[
// [values, shouldThrow]
["invalid", true],
[[1,[],{},"invalid"], false],
[[0,NaN,null,undefined,""], false],
[[true,true], false],
[[false,false,false], false],
].forEach(function([values, shouldThrow]) {
// Initialize
let oldValues = b.slice();
let oldLen = b.length;
setCallbackCount = 0;
deleteCallbackCount = 0;
setCallbackTests = null;
deleteCallbackTests = null;
if (!shouldThrow) {
let setCallbackIndex = 0;
setCallbackTests = function(_value, _index) {
info(`set callback for ${_index}`);
is(_value, !!values[setCallbackIndex], "setCallbackTests: test value argument");
is(_index, setCallbackIndex, "setCallbackTests: test index argument");
setCallbackIndex++;
};
let deleteCallbackIndex = b.length - 1;
deleteCallbackTests = function(_value, _index) {
info(`delete callback for ${_index}`);
is(_value, oldValues[deleteCallbackIndex], "deleteCallbackTests: test value argument");
is(_index, deleteCallbackIndex, "deleteCallbackTests: test index argument");
deleteCallbackIndex--;
};
}
// Test
info(`setting value to ${JSON.stringify(values)}`);
try {
m.observableArrayBoolean = values;
ok(!shouldThrow, `setting value should not throw`);
} catch(e) {
ok(shouldThrow, `setting value throws ${e}`);
}
is(setCallbackCount, shouldThrow ? 0 : values.length, "setCallback count");
is(deleteCallbackCount, oldLen, "deleteCallback should be called");
isDeeply(b, shouldThrow ? [] : values.map(v => !!v), "property values");
is(b.length, shouldThrow ? 0 : values.length, `length of observable array`);
});
});
add_task(function testObservableArray_setter_invalid_item() {
let setCallbackCount = 0;
let deleteCallbackCount = 0;
let setCallbackTests = null;
let deleteCallbackTests = null;
let m = new TestInterfaceObservableArray({
setInterfaceCallback(value, index) {
setCallbackCount++;
if (typeof setCallbackTests === 'function') {
setCallbackTests(value, index);
}
},
deleteInterfaceCallback(value, index) {
deleteCallbackCount++;
if (typeof deleteCallbackTests === 'function') {
deleteCallbackTests(value, index);
}
},
});
let b = m.observableArrayInterface;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 0, "length of observable array should be 0");
[
// [values, shouldThrow]
[[m,m,m,m], false],
[["invalid"], true],
[[m,m], false],
[[m,"invalid"], true],
[[m,m,m], false],
].forEach(function([values, shouldThrow]) {
// Initialize
let oldValues = b.slice();
let oldLen = b.length;
let setCallbackIndex = 0;
setCallbackTests = function(_value, _index) {
info(`set callback for ${_index}`);
is(_value, values[setCallbackIndex], "setCallbackTests: test value argument");
is(_index, setCallbackIndex, "setCallbackTests: test index argument");
setCallbackIndex++;
};
let deleteCallbackIndex = b.length - 1;
deleteCallbackTests = function(_value, _index) {
info(`delete callback for ${_index}`);
is(_value, oldValues[deleteCallbackIndex], "deleteCallbackTests: test value argument");
is(_index, deleteCallbackIndex, "deleteCallbackTests: test index argument");
deleteCallbackIndex--;
};
setCallbackCount = 0;
deleteCallbackCount = 0;
// Test
info(`setting value to ${values}`);
try {
m.observableArrayInterface = values;
ok(!shouldThrow, `setting value should not throw`);
} catch(e) {
ok(shouldThrow, `setting value throws ${e}`);
}
is(setCallbackCount, shouldThrow ? 0 : values.length, "setCallback count");
is(deleteCallbackCount, shouldThrow ? 0 : oldLen, "deleteCallback should be called");
isDeeply(b, shouldThrow ? oldValues : values, "property values");
is(b.length, shouldThrow ? oldLen : values.length, `length of observable array`);
});
});
add_task(function testObservableArray_setter_callback_throw() {
let setCallbackCount = 0;
let deleteCallbackCount = 0;
let m = new TestInterfaceObservableArray({
setBooleanCallback(value, index) {
setCallbackCount++;
if (index >= 3) {
throw new Error("setBooleanCallback");
}
},
deleteBooleanCallback(value, index) {
deleteCallbackCount++;
if (value) {
throw new Error("deleteBooleanCallback");
}
},
});
m.observableArrayBoolean = [false, false, false];
let b = m.observableArrayBoolean;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 3, "length of observable array should be 3");
[
// [values, shouldThrow, expectedLength, expectedSetCbCount, expectedDeleteCbCount]
[[false,false], false, 2, 2, 3],
[[false,true,false,false], true, 3, 4, 2],
[[false,false,true], true, 2, 0, 2],
].forEach(function([values, shouldThrow, expectedLength, expectedSetCbCount,
expectedDeleteCbCount]) {
// Initialize
setCallbackCount = 0;
deleteCallbackCount = 0;
// Test
info(`setting value to ${values}`);
try {
m.observableArrayBoolean = values;
ok(!shouldThrow, `setting value should not throw`);
} catch(e) {
ok(shouldThrow, `setting length throws ${e}`);
}
is(setCallbackCount, expectedSetCbCount, "setCallback should be called");
is(deleteCallbackCount, expectedDeleteCbCount, "deleteCallback should be called");
is(b.length, expectedLength, `length of observable array`);
});
});
add_task(function testObservableArray_indexed_setter() {
let setCallbackCount = 0;
let deleteCallbackCount = 0;
let setCallbackTests = null;
let deleteCallbackTests = null;
let m = new TestInterfaceObservableArray({
setBooleanCallback(value, index) {
setCallbackCount++;
if (typeof setCallbackTests === 'function') {
setCallbackTests(value, index);
}
},
deleteBooleanCallback(value, index) {
deleteCallbackCount++;
if (typeof deleteCallbackTests === 'function') {
deleteCallbackTests(value, index);
}
},
});
let b = m.observableArrayBoolean;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 0, "length of observable array should be 0");
[
// [index, value, expectedResult]
[b.length + 1, false, false],
[b.length, false, true],
[b.length + 1, false, true],
[b.length + 1, true, true],
].forEach(function([index, value, expectedResult]) {
// Initialize
let oldValue = b[index];
let oldLen = b.length;
setCallbackCount = 0;
deleteCallbackCount = 0;
setCallbackTests = function(_value, _index) {
info(`set callback for ${_index}`);
is(_value, value, "setCallbackTests: test value argument");
is(_index, index, "setCallbackTests: test index argument");
};
deleteCallbackTests = function(_value, _index) {
info(`delete callback for ${_index}`);
is(_value, oldValue, "deleteCallbackTests: test value argument");
is(_index, index, "deleteCallbackTests: test index argument");
};
// Test
info(`setting value of property ${index} to ${value}`);
try {
b[index] = value;
ok(true, `setting value should not throw`);
} catch(e) {
ok(false, `setting value throws ${e}`);
}
is(setCallbackCount, expectedResult ? 1 : 0, "setCallback should be called");
is(deleteCallbackCount, (oldLen > index) ? 1 : 0, "deleteCallback should be called");
is(b[index], expectedResult ? value : oldValue, `property value`);
is(b.length, expectedResult ? Math.max(oldLen, index + 1) : oldLen, `length of observable array`);
});
});
add_task(function testObservableArray_indexed_setter_invalid() {
let setCallbackCount = 0;
let deleteCallbackCount = 0;
let setCallbackTests = null;
let deleteCallbackTests = null;
let m = new TestInterfaceObservableArray({
setInterfaceCallback(value, index) {
setCallbackCount++;
if (typeof setCallbackTests === 'function') {
setCallbackTests(value, index);
}
},
deleteInterfaceCallback(value, index) {
deleteCallbackCount++;
if (typeof deleteCallbackTests === 'function') {
deleteCallbackTests(value, index);
}
},
});
let b = m.observableArrayInterface;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 0, "length of observable array should be 0");
[
// [index, value, shouldThrow]
[b.length, "invalid", true],
[b.length, m, false],
[b.length + 1, m, false],
[b.length + 1, "invalid", true],
].forEach(function([index, value, shouldThrow]) {
// Initialize
let oldValue = b[index];
let oldLen = b.length;
setCallbackCount = 0;
deleteCallbackCount = 0;
setCallbackTests = function(_value, _index) {
info(`set callback for ${_index}`);
is(_value, value, "setCallbackTests: test value argument");
is(_index, index, "setCallbackTests: test index argument");
};
deleteCallbackTests = function(_value, _index) {
info(`delete callback for ${_index}`);
is(_value, oldValue, "deleteCallbackTests: test value argument");
is(_index, index, "deleteCallbackTests: test index argument");
};
// Test
info(`setting value of property ${index} to ${value}`);
try {
b[index] = value;
ok(!shouldThrow, `setting value should not throw`);
} catch(e) {
ok(shouldThrow, `setting value throws ${e}`);
}
is(setCallbackCount, shouldThrow ? 0 : 1, "setCallback count");
is(deleteCallbackCount, ((oldLen > index) && !shouldThrow) ? 1 : 0, "deleteCallback count");
is(b[index], shouldThrow ? oldValue : value, `property value`);
is(b.length, shouldThrow ? oldLen : Math.max(oldLen, index + 1), `length of observable array`);
});
});
add_task(function testObservableArray_indexed_setter_callback_throw() {
let setCallbackCount = 0;
let deleteCallbackCount = 0;
let m = new TestInterfaceObservableArray({
setBooleanCallback(value, index) {
setCallbackCount++;
if (value) {
throw new Error("setBooleanCallback");
}
},
deleteBooleanCallback(value, index) {
deleteCallbackCount++;
if (index < 2) {
throw new Error("deleteBooleanCallback");
}
},
});
m.observableArrayBoolean = [false, false, false];
let b = m.observableArrayBoolean;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 3, "length of observable array should be 3");
[
// [index, value, shouldThrow]
[b.length, true, true],
[b.length, false, false],
[b.length, true, true],
[0, false, true],
[0, true, true]
].forEach(function([index, value, shouldThrow]) {
// Initialize
let oldValue = b[index];
let oldLen = b.length;
setCallbackCount = 0;
deleteCallbackCount = 0;
// Test
info(`setting value of property ${index} to ${value}`);
try {
b[index] = value;
ok(!shouldThrow, `setting value should not throw`);
} catch(e) {
ok(shouldThrow, `setting value throws ${e}`);
}
is(setCallbackCount, (shouldThrow && index < 2) ? 0 : 1, "setCallback should be called");
is(deleteCallbackCount, (oldLen > index) ? 1 : 0, "deleteCallback should be called");
is(b[index], shouldThrow ? oldValue : value, "property value");
is(b.length, shouldThrow ? oldLen : Math.max(oldLen, index + 1), `length of observable array`);
});
});
add_task(function testObservableArray_object() {
let setCallbackCount = 0;
let deleteCallbackCount = 0;
let callbackIndex = 0;
let values = [
{property1: false, property2: "property2"},
{property1: [], property2: 2},
];
let m = new TestInterfaceObservableArray({
setObjectCallback(value, index) {
setCallbackCount++;
is(index, callbackIndex++, "setCallbackTests: test index argument");
isDeeply(values[index], value, "setCallbackTests: test value argument");
},
deleteObjectCallback(value, index) {
deleteCallbackCount++;
},
});
m.observableArrayObject = values;
let b = m.observableArrayObject;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 2, "length of observable array should be 2");
is(setCallbackCount, values.length, "setCallback should be called");
is(deleteCallbackCount, 0, "deleteCallback should not be called");
for(let i = 0; i < values.length; i++) {
isDeeply(values[i], b[i], `check index ${i}`);
}
});
</script>
</body>
</html>

View file

@ -0,0 +1,376 @@
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<!DOCTYPE HTML>
<html>
<head>
<title>Test Helpers of Observable Array Type</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<script>
/* global TestInterfaceObservableArray */
add_task(async function init() {
await SpecialPowers.pushPrefEnv({set: [["dom.expose_test_interfaces", true]]});
});
add_task(function testObservableArray_helper_elementAt() {
let m = new TestInterfaceObservableArray();
[
// [values, property, helper]
[[true, false], "observableArrayBoolean", m.booleanElementAtInternal.bind(m)],
[[new TestInterfaceObservableArray(), new TestInterfaceObservableArray()],
"observableArrayInterface", m.interfaceElementAtInternal.bind(m)],
[[{property: "test"}, {property: 2}], "observableArrayObject",
m.objectElementAtInternal.bind(m)],
].forEach(function([values, property, helper]) {
m[property] = values;
let t = m[property];
ok(Array.isArray(t), "observable array should be an array type");
is(t.length, values.length, "length of observable array");
for (let i = 0; i < values.length; i++) {
isDeeply(values[i], helper(i), `check index ${i}`);
}
SimpleTest.doesThrow(() => {
helper(values.length);
}, `getting element outside the range should throw`);
});
});
add_task(function testObservableArray_helper_replaceElementAt() {
let setCallbackCount = 0;
let deleteCallbackCount = 0;
let setCallbackTests = null;
let deleteCallbackTests = null;
let m = new TestInterfaceObservableArray({
setBooleanCallback(value, index) {
setCallbackCount++;
if (typeof setCallbackTests === 'function') {
setCallbackTests(value, index);
}
},
deleteBooleanCallback(value, index) {
deleteCallbackCount++;
if (typeof deleteCallbackTests === 'function') {
deleteCallbackTests(value, index);
}
},
});
let b = m.observableArrayBoolean;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 0, "length of observable array should be 0");
[
// [index, value, shouldThrow]
[b.length + 1, false, true],
[b.length, false, false],
[b.length + 1, false, false],
[b.length + 1, true, false],
].forEach(function([index, value, shouldThrow]) {
// Initialize
let oldValue = b[index];
let oldLen = b.length;
setCallbackCount = 0;
deleteCallbackCount = 0;
setCallbackTests = function(_value, _index) {
info(`set callback for ${_index}`);
is(_value, value, "setCallbackTests: test value argument");
is(_index, index, "setCallbackTests: test index argument");
};
deleteCallbackTests = function(_value, _index) {
info(`delete callback for ${_index}`);
is(_value, oldValue, "deleteCallbackTests: test value argument");
is(_index, index, "deleteCallbackTests: test index argument");
};
// Test
info(`setting value of property ${index} to ${value}`);
try {
m.booleanReplaceElementAtInternal(index, value);
ok(!shouldThrow, `setting value should not throw`);
} catch(e) {
ok(shouldThrow, `setting value throws ${e}`);
}
is(setCallbackCount, shouldThrow ? 0 : 1, "setCallback count");
is(deleteCallbackCount, (oldLen > index) ? 1 : 0, "deleteCallback count");
is(b[index], shouldThrow ? oldValue : value, `property value`);
is(b.length, shouldThrow ? oldLen : Math.max(oldLen, index + 1), `length of observable array`);
});
});
add_task(function testObservableArray_helper_replaceElementAt_callback_throw() {
let setCallbackCount = 0;
let deleteCallbackCount = 0;
let m = new TestInterfaceObservableArray({
setBooleanCallback(value, index) {
setCallbackCount++;
if (value) {
throw new Error("setBooleanCallback");
}
},
deleteBooleanCallback(value, index) {
deleteCallbackCount++;
if (index < 2) {
throw new Error("deleteBooleanCallback");
}
},
});
m.observableArrayBoolean = [false, false, false];
let b = m.observableArrayBoolean;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 3, "length of observable array should be 3");
[
// [index, value, shouldThrow]
[b.length, true, true],
[b.length, false, false],
[b.length, true, true],
[0, false, true],
[0, true, true]
].forEach(function([index, value, shouldThrow]) {
// Initialize
let oldValue = b[index];
let oldLen = b.length;
setCallbackCount = 0;
deleteCallbackCount = 0;
// Test
info(`setting value of property ${index} to ${value}`);
try {
m.booleanReplaceElementAtInternal(index, value);
ok(!shouldThrow, `setting value should not throw`);
} catch(e) {
ok(shouldThrow, `setting value throws ${e}`);
}
is(setCallbackCount, (shouldThrow && index < 2) ? 0 : 1, "setCallback count");
is(deleteCallbackCount, (oldLen > index) ? 1 : 0, "deleteCallback count");
is(b[index], shouldThrow ? oldValue : value, "property value");
is(b.length, shouldThrow ? oldLen : Math.max(oldLen, index + 1), `length of observable array`);
});
});
add_task(function testObservableArray_helper_appendElement() {
let setCallbackCount = 0;
let deleteCallbackCount = 0;
let setCallbackTests = null;
let m = new TestInterfaceObservableArray({
setBooleanCallback(value, index) {
setCallbackCount++;
if (typeof setCallbackTests === 'function') {
setCallbackTests(value, index);
}
},
deleteBooleanCallback(value, index) {
deleteCallbackCount++;
},
});
let b = m.observableArrayBoolean;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 0, "length of observable array should be 0");
[true, false, true, false].forEach(function(value) {
// Initialize
let oldLen = b.length;
let index = oldLen;
setCallbackCount = 0;
deleteCallbackCount = 0;
setCallbackTests = function(_value, _index) {
info(`set callback for ${_index}`);
is(_value, value, "setCallbackTests: test value argument");
is(_index, index, "setCallbackTests: test index argument");
};
// Test
info(`append ${value}`);
try {
m.booleanAppendElementInternal(value);
ok(true, `appending value should not throw`);
} catch(e) {
ok(false, `appending value throws ${e}`);
}
is(setCallbackCount, 1, "setCallback should be called");
is(deleteCallbackCount, 0, "deleteCallback should not be called");
is(b[index], value, `property value`);
is(b.length, oldLen + 1, `length of observable array`);
});
});
add_task(function testObservableArray_helper_appendElement_callback_throw() {
let setCallbackCount = 0;
let deleteCallbackCount = 0;
let m = new TestInterfaceObservableArray({
setBooleanCallback(value, index) {
setCallbackCount++;
if (value) {
throw new Error("setBooleanCallback");
}
},
deleteBooleanCallback(value, index) {
deleteCallbackCount++;
},
});
m.observableArrayBoolean = [false, false, false];
let b = m.observableArrayBoolean;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 3, "length of observable array should be 3");
[true, false, true, false].forEach(function(value) {
// Initialize
let oldLen = b.length;
let index = oldLen;
let oldValue = b[index];
let shouldThrow = value;
setCallbackCount = 0;
deleteCallbackCount = 0;
// Test
info(`append ${value}`);
try {
m.booleanAppendElementInternal(value);
ok(!shouldThrow, `appending value should not throw`);
} catch(e) {
ok(shouldThrow, `appending value throws ${e}`);
}
is(setCallbackCount, 1, "setCallback should be called");
is(deleteCallbackCount, 0, "deleteCallback should not be called");
is(b[index], shouldThrow ? oldValue : value, "property value");
is(b.length, shouldThrow ? oldLen : oldLen + 1, `length of observable array`);
});
});
add_task(function testObservableArray_helper_removeLastElement() {
let setCallbackCount = 0;
let deleteCallbackCount = 0;
let deleteCallbackTests = null;
let m = new TestInterfaceObservableArray({
setBooleanCallback(value, index) {
setCallbackCount++;
},
deleteBooleanCallback(value, index) {
deleteCallbackCount++;
if (typeof deleteCallbackTests === 'function') {
deleteCallbackTests(value, index);
}
},
});
m.observableArrayBoolean = [true, false, true, false];
let b = m.observableArrayBoolean;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 4, "length of observable array should be 4");
let oldValues = b.slice();
while (oldValues.length) {
// Initialize
let oldValue = oldValues.pop();
let index = oldValues.length;
setCallbackCount = 0;
deleteCallbackCount = 0;
deleteCallbackTests = function(_value, _index) {
info(`delete callback for ${_index}`);
is(_value, oldValue, "deleteCallbackTests: test value argument");
is(_index, index, "deleteCallbackTests: test index argument");
};
// Test
info(`remove last element`);
try {
m.booleanRemoveLastElementInternal();
ok(true, `removing last element should not throw`);
} catch(e) {
ok(false, `removing last element throws ${e}`);
}
is(setCallbackCount, 0, "setCallback should not be called");
is(deleteCallbackCount, 1, "deleteCallback should be called");
isDeeply(b, oldValues, `property value`);
is(b.length, oldValues.length, `length of observable array`);
}
// test when array is empty
setCallbackCount = 0;
deleteCallbackCount = 0;
SimpleTest.doesThrow(() => {
m.booleanRemoveLastElementInternal();
}, `removing last element should throw when array is empty`);
is(setCallbackCount, 0, "setCallback should not be called");
is(deleteCallbackCount, 0, "deleteCallback should not be called");
is(b.length, 0, `length of observable array`);
});
add_task(function testObservableArray_helper_removeLastElement_callback_throw() {
let setCallbackCount = 0;
let deleteCallbackCount = 0;
let m = new TestInterfaceObservableArray({
setBooleanCallback(value, index) {
setCallbackCount++;
},
deleteBooleanCallback(value, index) {
deleteCallbackCount++;
if (value) {
throw new Error("deleteBooleanCallback");
}
},
});
m.observableArrayBoolean = [false, true, false, false];
let b = m.observableArrayBoolean;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 4, "length of observable array should be 4");
let shouldThrow = false;
while (!shouldThrow && b.length) {
// Initialize
let oldValues = b.slice();
let oldLen = b.length;
shouldThrow = oldValues[oldLen - 1];
setCallbackCount = 0;
deleteCallbackCount = 0;
// Test
info(`remove last element`);
try {
m.booleanRemoveLastElementInternal();
ok(!shouldThrow, `removing last element should not throw`);
} catch(e) {
ok(shouldThrow, `removing last element throws ${e}`);
}
is(setCallbackCount, 0, "setCallback should not be called");
is(deleteCallbackCount, 1, "deleteCallback should be called");
isDeeply(b, shouldThrow ? oldValues : oldValues.slice(0, oldLen - 1), `property value`);
is(b.length, shouldThrow ? oldLen : oldLen - 1, `length of observable array`);
}
});
add_task(function testObservableArray_helper_length() {
let m = new TestInterfaceObservableArray();
let b = m.observableArrayBoolean;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 0, "length of observable array");
[
[false, true, false, true],
[true, false],
[false, true, false],
].forEach(function(values) {
m.observableArrayBoolean = values;
is(b.length, m.booleanLengthInternal(), "length helper function");
});
});
</script>
</body>
</html>

View file

@ -0,0 +1,859 @@
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<!DOCTYPE HTML>
<html>
<head>
<title>Test Observable Array Type</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<script>
/* global TestInterfaceObservableArray */
add_task(async function init() {
await SpecialPowers.pushPrefEnv({set: [["dom.expose_test_interfaces", true]]});
});
add_task(function testObservableArrayExoticObjects_defineProperty() {
let setCallbackCount = 0;
let deleteCallbackCount = 0;
let setCallbackTests = null;
let deleteCallbackTests = null;
let m = new TestInterfaceObservableArray({
setBooleanCallback(value, index) {
setCallbackCount++;
if (typeof setCallbackTests === 'function') {
setCallbackTests(value, index);
}
},
deleteBooleanCallback(value, index) {
deleteCallbackCount++;
if (typeof deleteCallbackTests === 'function') {
deleteCallbackTests(value, index);
}
},
});
m.observableArrayBoolean = [true, true, true];
let b = m.observableArrayBoolean;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 3, "length of observable array should be 0");
// Test length
[
// [descriptor, shouldThrow, expectedResult]
// Invalid descriptor
[{configurable: true, value: 0}, false, false],
[{enumerable: true, value: 0}, false, false],
[{writable: false, value: 0}, false, false],
[{get: ()=>{}}, false, false],
[{set: ()=>{}}, false, false],
[{get: ()=>{}, set: ()=>{}}, false, false],
[{get: ()=>{}, value: 0}, true],
// Invalid length value
[{value: 1.9}, true],
[{value: "invalid"}, true],
[{value: {}}, true],
// length value should not greater than current length
[{value: b.length + 1}, false, false],
// descriptor without value
[{configurable: false, enumerable: false, writable: true}, false, true],
// Success
[{value: b.length}, false, true],
[{value: b.length - 1}, false, true],
[{value: 0}, false, true],
].forEach(function([descriptor, shouldThrow, expectedResult]) {
// Initialize
let oldLen = b.length;
let oldValues = b.slice();
let deleteCallbackIndex = oldLen - 1;
let success = expectedResult && "value" in descriptor;
setCallbackCount = 0;
deleteCallbackCount = 0;
setCallbackTests = null;
deleteCallbackTests = function(_value, _index) {
is(_value, oldValues[deleteCallbackIndex], "deleteCallbackTests: test value argument");
is(_index, deleteCallbackIndex, "deleteCallbackTests: test index argument");
deleteCallbackIndex--;
};
// Test
info(`defining "length" property with ${JSON.stringify(descriptor)}`);
try {
is(Reflect.defineProperty(b, "length", descriptor), expectedResult,
`Reflect.defineProperty should return ${expectedResult}`);
ok(!shouldThrow, "Reflect.defineProperty should not throw");
} catch(e) {
ok(shouldThrow, `Reflect.defineProperty throws ${e}`);
}
is(setCallbackCount, 0, "setCallback count");
is(deleteCallbackCount, success ? oldLen - descriptor.value : 0, "deleteCallback count");
isDeeply(b, success ? oldValues.slice(0, descriptor.value) : oldValues, "property values");
is(b.length, success ? descriptor.value : oldLen, "length of observable array");
});
// Test indexed value
[
// [index, descriptor, shouldThrow, expectedResult]
// Invalid descriptor
[0, {configurable: false, value: true}, false, false],
[0, {enumerable: false, value: true}, false, false],
[0, {writable: false, value: true}, false, false],
[0, {get: ()=>{}}, false, false],
[0, {set: ()=>{}}, false, false],
[0, {get: ()=>{}, set: ()=>{}}, false, false],
[0, {get: ()=>{}, value: true}, true],
// Index could not greater than last index + 1.
[b.length + 1, {configurable: true, enumerable: true, value: true}, false, false],
// descriptor without value
[b.length, {configurable: true, enumerable: true}, false, true],
// Success
[b.length, {configurable: true, enumerable: true, value: true}, false, true],
[b.length + 1, {configurable: true, enumerable: true, value: true}, false, true],
].forEach(function([index, descriptor, shouldThrow, expectedResult]) {
// Initialize
let oldLen = b.length;
let oldValue = b[index];
let success = expectedResult && "value" in descriptor;
setCallbackCount = 0;
deleteCallbackCount = 0;
setCallbackTests = function(_value, _index) {
is(_value, descriptor.value, "setCallbackTests: test value argument");
is(_index, index, "setCallbackTests: test index argument");
};
deleteCallbackTests = function(_value, _index) {
is(_value, oldValue, "deleteCallbackTests: test value argument");
is(_index, index, "deleteCallbackTests: test index argument");
};
// Test
info(`defining ${index} property with ${JSON.stringify(descriptor)}`);
try {
is(Reflect.defineProperty(b, index, descriptor), expectedResult,
`Reflect.defineProperty should return ${expectedResult}`);
ok(!shouldThrow, "Reflect.defineProperty should not throw");
} catch(e) {
ok(shouldThrow, `Reflect.defineProperty throws ${e}`);
}
is(setCallbackCount, success ? 1 : 0, "setCallback count");
is(deleteCallbackCount, (oldLen > index) ? 1 : 0, "deleteCallback count");
is(b[index], success ? descriptor.value : oldValue, "property value");
is(b.length, success ? Math.max(index + 1, oldLen) : oldLen, "length of observable array");
});
// Test other property
[
// [property, descriptor, shouldThrow, expectedResult]
["prop1", {configurable: false, value: "value1"}, false, true],
["prop1", {configurable: true, value: "value2"}, false, false],
["prop2", {enumerable: false, value: 5}, false, true],
["prop3", {enumerable: false, value: []}, false, true],
["prop4", {enumerable: false, value: {}}, false, true],
["prop5", {get: ()=>{}, value: true}, true, false],
["prop6", {get: ()=>{}, set: ()=>{}}, false, true],
].forEach(function([property, descriptor, shouldThrow, expectedResult]) {
// Initialize
let oldValue = b[property];
let oldLen = b.length;
setCallbackCount = 0;
deleteCallbackCount = 0;
setCallbackTests = null;
deleteCallbackTests = null;
// Test
info(`defining ${property} property with ${JSON.stringify(descriptor)}`);
try {
is(Reflect.defineProperty(b, property, descriptor), expectedResult,
`Reflect.defineProperty should return ${expectedResult}`);
ok(!shouldThrow, "Reflect.defineProperty should not throw");
} catch(e) {
ok(shouldThrow, `Reflect.defineProperty throws ${e}`);
}
is(setCallbackCount, 0, "setCallback count");
is(deleteCallbackCount, 0, "deleteCallback count");
is(b[property], expectedResult ? descriptor.value : oldValue, "property value");
is(b.length, oldLen, "length of observable array");
});
});
add_task(function testObservableArrayExoticObjects_defineProperty_callback_throw() {
let setCallbackCount = 0;
let deleteCallbackCount = 0;
const minLen = 3;
let m = new TestInterfaceObservableArray({
setBooleanCallback(value, index) {
setCallbackCount++;
if (value) {
throw new Error("setBooleanCallback");
}
},
deleteBooleanCallback(value, index) {
deleteCallbackCount++;
if (index < minLen) {
throw new Error("deleteBooleanCallback");
}
},
});
m.observableArrayBoolean = [false, false, false, false, false];
let b = m.observableArrayBoolean;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 5, "length of observable array should be 3");
// Test length
[
// [length, shouldThrow]
[b.length, false],
[b.length - 1, false],
[0, true],
].forEach(function([length, shouldThrow]) {
// Initialize
let oldValues = b.slice();
let oldLen = b.length;
let descriptor = {value: length};
setCallbackCount = 0;
deleteCallbackCount = 0;
// Test
info(`defining "length" property with ${JSON.stringify(descriptor)}`);
try {
ok(Reflect.defineProperty(b, "length", descriptor),
"Reflect.defineProperty should return true");
ok(!shouldThrow, "Reflect.defineProperty should not throw");
} catch(e) {
ok(shouldThrow, `Reflect.defineProperty throws ${e}`);
}
is(setCallbackCount, 0, "setCallback count");
is(deleteCallbackCount, oldLen - (shouldThrow ? minLen - 1 : length), "deleteCallback count");
isDeeply(b, oldValues.slice(0, shouldThrow ? minLen : length), "property values");
is(b.length, shouldThrow ? minLen : length, "length of observable array");
});
// Test indexed value
[
// [index, value, shouldThrow]
[b.length, true, true],
[b.length, false, false],
[b.length + 1, false, false],
[b.length + 1, true, true],
[0, true, true],
[0, false, true],
].forEach(function([index, value, shouldThrow]) {
// Initialize
let oldValue = b[index];
let oldLen = b.length;
let descriptor = {configurable: true, enumerable: true, value};
setCallbackCount = 0;
deleteCallbackCount = 0;
// Test
info(`defining ${index} property with ${JSON.stringify(descriptor)}`);
try {
ok(Reflect.defineProperty(b, index, descriptor), "Reflect.defineProperty should return true");
ok(!shouldThrow, "Reflect.defineProperty should not throw");
} catch(e) {
ok(shouldThrow, `Reflect.defineProperty throws ${e}`);
}
is(setCallbackCount, (index < minLen) ? 0 : 1, "setCallback count");
is(deleteCallbackCount, (oldLen > index) ? 1 : 0, "deleteCallback count");
is(b[index], shouldThrow ? oldValue : value, "property value");
is(b.length, shouldThrow ? oldLen : Math.max(oldLen, index + 1), "length of observable array");
});
// Test other property
[
// [property, descriptor, expectedResult]
["prop1", {configurable: false, value: "value1"}, true],
["prop1", {configurable: true, value: "value2"}, false],
["prop2", {enumerable: false, value: 5}, true],
["prop3", {enumerable: false, value: []}, true],
["prop4", {enumerable: false, value: {}}, true],
].forEach(function([property, descriptor, expectedResult]) {
// Initialize
let oldValue = b[property];
let oldLen = b.length;
setCallbackCount = 0;
deleteCallbackCount = 0;
// Test
info(`defining ${property} property with ${JSON.stringify(descriptor)}`);
try {
is(Reflect.defineProperty(b, property, descriptor), expectedResult,
`Reflect.defineProperty should return ${expectedResult}`);
ok(true, "Reflect.defineProperty should not throw");
} catch(e) {
ok(false, `Reflect.defineProperty throws ${e}`);
}
is(setCallbackCount, 0, "setCallback count");
is(deleteCallbackCount, 0, "deleteCallback count");
is(b[property], expectedResult ? descriptor.value : oldValue, "property value");
is(b.length, oldLen, "length of observable array");
});
});
add_task(function testObservableArrayExoticObjects_deleteProperty() {
let setCallbackCount = 0;
let deleteCallbackCount = 0;
let deleteCallbackTests = null;
let m = new TestInterfaceObservableArray({
setBooleanCallback(value, index) {
setCallbackCount++;
},
deleteBooleanCallback(value, index) {
deleteCallbackCount++;
if (typeof deleteCallbackTests === 'function') {
deleteCallbackTests(value, index);
}
},
});
m.observableArrayBoolean = [true, true];
let b = m.observableArrayBoolean;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 2, "length of observable array should be 2");
// Test length
setCallbackCount = 0;
deleteCallbackCount = 0;
info("deleting length property");
ok(!Reflect.deleteProperty(b, "length"), "test result of deleting length property");
is(setCallbackCount, 0, "setCallback should not be called");
is(deleteCallbackCount, 0, "deleteCallback should not be called");
is(b.length, 2, "length should still be 2");
// Test indexed value
[
// [index, expectedResult]
[2, false],
[0, false],
[1, true],
].forEach(function([index, expectedResult]) {
// Initialize
let oldLen = b.length;
let oldValue = b[index];
setCallbackCount = 0;
deleteCallbackCount = 0;
deleteCallbackTests = function(_value, _index) {
is(_value, oldValue, "deleteCallbackTests: test value argument");
is(_index, index, "deleteCallbackTests: test index argument");
};
// Test
info(`deleting ${index} property`);
is(Reflect.deleteProperty(b, index), expectedResult,
`Reflect.deleteProperty should return ${expectedResult}`);
is(setCallbackCount, 0, "setCallback count");
is(deleteCallbackCount, expectedResult ? 1 : 0, "deleteCallback count");
is(b[index], expectedResult ? undefined : oldValue, "property value");
is(b.length, expectedResult ? oldLen - 1 : oldLen,
"length of observable array");
});
// Test other property
[
// [property, value]
["prop1", "value1"],
["prop2", 5],
["prop3", []],
["prop4", {}],
].forEach(function([property, value]) {
// Initialize
b[property] = value;
let oldLen = b.length;
setCallbackCount = 0;
deleteCallbackCount = 0;
deleteCallbackTests = null;
// Test
info(`deleting ${property} property`);
is(b[property], value, `property value should be ${value} before deleting`);
ok(Reflect.deleteProperty(b, property), "Reflect.deleteProperty should return true");
is(setCallbackCount, 0, "setCallback count");
is(deleteCallbackCount, 0, "deleteCallback count");
is(b[property], undefined, "property value should be undefined after deleting");
is(b.length, oldLen, "length of observable array");
});
});
add_task(function testObservableArrayExoticObjects_deleteProperty_callback_throw() {
let setCallbackCount = 0;
let deleteCallbackCount = 0;
let m = new TestInterfaceObservableArray({
setBooleanCallback(value, index) {
setCallbackCount++;
},
deleteBooleanCallback(value, index) {
deleteCallbackCount++;
if (value) {
throw new Error("deleteBooleanCallback");
}
},
});
m.observableArrayBoolean = [true, false];
let b = m.observableArrayBoolean;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 2, "length of observable array should be 2");
// Test indexed value
let index = b.length;
while (index--) {
// Initialize
let oldValue = b[index];
let oldLen = b.length;
setCallbackCount = 0;
deleteCallbackCount = 0;
// Test
info(`deleting index ${index}`);
try {
ok(Reflect.deleteProperty(b, index), "Reflect.deleteProperty should return true");
ok(!oldValue, "Reflect.deleteProperty should not throw");
} catch(e) {
ok(oldValue, `Reflect.deleteProperty throws ${e}`);
}
is(setCallbackCount, 0, "setCallback count");
is(deleteCallbackCount, 1, "deleteCallback count");
is(b[index], oldValue ? oldValue : undefined, "property value");
is(b.length, oldValue ? oldLen : oldLen - 1, "length of observable array");
}
// Test other property
[
// [property, value]
["prop1", "value1"],
["prop2", 5],
["prop3", []],
["prop4", {}],
["prop5", false],
].forEach(function([property, value]) {
// Initialize
b[property] = value;
let oldLen = b.length;
setCallbackCount = 0;
deleteCallbackCount = 0;
// Test
info(`deleting ${property} property`);
is(b[property], value, `property value should be ${JSON.stringify(value)} before deleting`);
try {
ok(Reflect.deleteProperty(b, property), `Reflect.deleteProperty should return true`);
ok(true, "Reflect.deleteProperty should not throw");
} catch(e) {
ok(false, `Reflect.deleteProperty throws ${e}`);
}
is(setCallbackCount, 0, "setCallback count");
is(deleteCallbackCount, 0, "deleteCallback count");
is(b[property], undefined, `property value should be undefined after deleting`);
is(b.length, oldLen, "length of observable array");
});
});
add_task(function testObservableArrayExoticObjects_get() {
let m = new TestInterfaceObservableArray();
m.observableArrayBoolean = [true, false];
let b = m.observableArrayBoolean;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 2, "length of observable array should be 2");
// Test length
is(Reflect.get(b, "length"), 2, "test result of getting length property");
// Test indexed value
is(Reflect.get(b, 0), true, "test result of getting index 0");
is(Reflect.get(b, 1), false, "test result of getting index 1");
is(Reflect.get(b, 2), undefined, "test result of getting index 2");
// Test other property
[
// [property, value]
["prop1", "value1"],
["prop2", 5],
["prop3", []],
["prop4", {}],
].forEach(function([property, value]) {
is(Reflect.get(b, property), undefined, `test ${property} property before setting property value`);
b[property] = value;
is(Reflect.get(b, property), value, `test ${property} property after setting property value`);
});
});
add_task(function testObservableArrayExoticObjects_getOwnPropertyDescriptor() {
function TestDescriptor(object, property, exist, configurable, enumerable,
writable, value) {
let descriptor = Reflect.getOwnPropertyDescriptor(object, property);
if (!exist) {
is(descriptor, undefined, `descriptor of ${property} property should be undefined`);
return;
}
is(descriptor.configurable, configurable, `test descriptor of ${property} property (configurable)`);
is(descriptor.enumerable, enumerable, `test descriptor of ${property} property (enumerable)`);
is(descriptor.writable, writable, `test descriptor of ${property} property (writable)`);
is(descriptor.value, value, `test descriptor of ${property} property (value)`);
}
let m = new TestInterfaceObservableArray();
m.observableArrayBoolean = [true, false];
let b = m.observableArrayBoolean;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 2, "length of observable array should be 2");
// Test length
TestDescriptor(b, "length", true, false /* configurable */,
false /* enumerable */, true /* writable */ , 2 /* value */);
// Test indexed value
TestDescriptor(b, 0, true, true /* configurable */, true /* enumerable */,
true /* writable */ , true /* value */);
TestDescriptor(b, 1, true, true /* configurable */, true /* enumerable */,
true /* writable */ , false /* value */);
TestDescriptor(b, 2, false);
// Test other property
[
// [property, value, configurable, enumerable, writable]
["prop1", "value1", true, true, true],
["prop2", 5, true, true, false],
["prop3", [], true, false, false],
["prop4", {}, false, false, false],
].forEach(function([property, value, configurable, enumerable, writable]) {
Object.defineProperty(b, property, {
value,
configurable,
enumerable,
writable,
});
TestDescriptor(b, property, true, configurable, enumerable, writable , value);
});
});
add_task(function testObservableArrayExoticObjects_has() {
let m = new TestInterfaceObservableArray();
m.observableArrayBoolean = [true, false];
let b = m.observableArrayBoolean;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 2, "length of observable array should be 2");
// Test length
ok(Reflect.has(b, "length"), `test length property`);
// Test indexed value
ok(Reflect.has(b, 0), `test 0 property`);
ok(Reflect.has(b, 1), `test 1 property`);
ok(!Reflect.has(b, 2), `test 2 property`);
// Test other property
[
// [property, value]
["prop1", "value1"],
["prop2", 5],
["prop3", []],
["prop4", {}],
].forEach(function([property, value]) {
ok(!Reflect.has(b, property), `test ${property} property before setting property value`);
b[property] = value;
ok(Reflect.has(b, property), `test ${property} property after setting property value`);
});
});
add_task(function testObservableArrayExoticObjects_ownKeys() {
let m = new TestInterfaceObservableArray();
m.observableArrayBoolean = [true, false];
let b = m.observableArrayBoolean;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 2, "length of observable array should be 2");
// Add other properties
b.prop1 = "value1";
b.prop2 = 5;
b.prop3 = [];
b.prop4 = {};
let keys = Reflect.ownKeys(b);
SimpleTest.isDeeply(keys, ["0", "1", "length", "prop1", "prop2", "prop3", "prop4"], `test property keys`);
});
add_task(function testObservableArrayExoticObjects_preventExtensions() {
let m = new TestInterfaceObservableArray();
let b = m.observableArrayBoolean;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 0, "length of observable array should be 0");
// Test preventExtensions
ok(Reflect.isExtensible(b), "test isExtensible before preventExtensions");
ok(!Reflect.preventExtensions(b), "test preventExtensions");
ok(Reflect.isExtensible(b), "test isExtensible after preventExtensions");
});
add_task(function testObservableArrayExoticObjects_set() {
let setCallbackCount = 0;
let deleteCallbackCount = 0;
let setCallbackTests = null;
let deleteCallbackTests = null;
let m = new TestInterfaceObservableArray({
setBooleanCallback(value, index) {
setCallbackCount++;
if (typeof setCallbackTests === 'function') {
setCallbackTests(value, index);
}
},
deleteBooleanCallback(value, index) {
deleteCallbackCount++;
if (typeof deleteCallbackTests === 'function') {
deleteCallbackTests(value, index);
}
},
});
m.observableArrayBoolean = [true, true, true];
let b = m.observableArrayBoolean;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 3, "length of observable array should be 3");
// Test length
[
// [length, shouldThrow, expectedResult]
// Invalid length value
[1.9, true],
['invalid', true],
[{}, true],
// length value should not greater than current length
[b.length + 1, false, false],
// Success
[b.length, false, true],
[b.length - 1, false, true],
[0, false, true],
].forEach(function([length, shouldThrow, expectedResult]) {
// Initialize
let oldLen = b.length;
let oldValues = b.slice();
setCallbackCount = 0;
deleteCallbackCount = 0;
setCallbackTests = null;
let deleteCallbackIndex = oldLen - 1;
deleteCallbackTests = function(_value, _index) {
is(_value, oldValues[deleteCallbackIndex], "deleteCallbackTests: test value argument");
is(_index, deleteCallbackIndex, "deleteCallbackTests: test index argument");
deleteCallbackIndex--;
};
// Test
info(`setting "length" property value to ${length}`);
try {
is(Reflect.set(b, "length", length), expectedResult, `Reflect.set should return ${expectedResult}`);
ok(!shouldThrow, "Reflect.set should not throw");
} catch(e) {
ok(shouldThrow, `Reflect.set throws ${e}`);
}
is(setCallbackCount, 0, "setCallback count");
is(deleteCallbackCount, expectedResult ? oldLen - length : 0, "deleteCallback count");
isDeeply(b, expectedResult ? oldValues.slice(0, length) : oldValues, "property values");
is(b.length, expectedResult ? length : oldLen, "length of observable array");
});
// Test indexed value
[
// [index, value, shouldThrow, expectedResult]
// Index could not greater than last index.
[b.length + 1, true, false, false],
// Success
[b.length, true, false, true],
[b.length + 1, true, false, true],
].forEach(function([index, value, shouldThrow, expectedResult]) {
// Initialize
let oldLen = b.length;
let oldValue = b[index];
setCallbackCount = 0;
deleteCallbackCount = 0;
setCallbackTests = function(_value, _index) {
is(_value, value, "setCallbackTests: test value argument");
is(_index, index, "setCallbackTests: test index argument");
};
deleteCallbackTests = function(_value, _index) {
is(_value, oldValue, "deleteCallbackTests: test value argument");
is(_index, index, "deleteCallbackTests: test index argument");
};
// Test
info(`setting ${index} property to ${value}`);
try {
is(Reflect.set(b, index, value), expectedResult, `Reflect.set should return ${expectedResult}`);
ok(!shouldThrow, "Reflect.set should not throw");
} catch(e) {
ok(shouldThrow, `Reflect.set throws ${e}`);
}
is(setCallbackCount, expectedResult ? 1 : 0, "setCallback count");
is(deleteCallbackCount, (oldLen > index) ? 1 : 0, "deleteCallback count");
is(b[index], expectedResult ? value : oldValue, "property value");
is(b.length, expectedResult ? Math.max(index + 1, oldLen) : oldLen, "length of observable array");
});
// Test other property
[
// [property, value]
["prop1", "value1"],
["prop1", "value2"],
["prop2", 5],
["prop3", []],
["prop4", {}],
].forEach(function([property, value]) {
// Initialize
let oldLen = b.length;
setCallbackCount = 0;
deleteCallbackCount = 0;
setCallbackTests = null;
deleteCallbackTests = null;
// Test
info(`setting ${property} property to ${value}`);
ok(Reflect.set(b, property, value), "Reflect.defineProperty should return true");
is(setCallbackCount, 0, "setCallback count");
is(deleteCallbackCount, 0, "deleteCallback count");
is(b[property], value, "property value");
is(b.length, oldLen, "length of observable array");
});
});
add_task(function testObservableArrayExoticObjects_set_callback_throw() {
let setCallbackCount = 0;
let deleteCallbackCount = 0;
const minLen = 3;
let m = new TestInterfaceObservableArray({
setBooleanCallback(value, index) {
setCallbackCount++;
if (value) {
throw new Error("setBooleanCallback");
}
},
deleteBooleanCallback(value, index) {
deleteCallbackCount++;
if (index < minLen) {
throw new Error("deleteBooleanCallback");
}
},
});
m.observableArrayBoolean = [false, false, false, false, false];
let b = m.observableArrayBoolean;
ok(Array.isArray(b), "observable array should be an array type");
is(b.length, 5, "length of observable array should be 3");
// Test length
[
// [value, shouldThrow]
[b.length, false],
[b.length - 1, false],
[0, true],
].forEach(function([length, shouldThrow]) {
// Initialize
let oldValues = b.slice();
let oldLen = b.length;
setCallbackCount = 0;
deleteCallbackCount = 0;
// Test
info(`setting "length" property to ${length}`);
try {
ok(Reflect.set(b, "length", length), "Reflect.set should return true");
ok(!shouldThrow, `Reflect.set should not throw`);
} catch(e) {
ok(shouldThrow, `Reflect.set throws ${e}`);
}
is(setCallbackCount, 0, "setCallback should not be called");
is(deleteCallbackCount, oldLen - (shouldThrow ? minLen - 1 : length), "deleteCallback count");
isDeeply(b, oldValues.slice(0, shouldThrow ? minLen : length), "property values");
is(b.length, shouldThrow ? minLen : length, "length of observable array");
});
// Test indexed value
[
// [index, value, shouldThrow]
[b.length, true, true],
[b.length, false, false],
[b.length + 1, false, false],
[b.length + 1, true, true],
[0, false, true],
[0, true, true],
].forEach(function([index, value, shouldThrow]) {
// Initialize
let oldValue = b[index];
let oldLen = b.length;
setCallbackCount = 0;
deleteCallbackCount = 0;
// Test
info(`setting ${index} property to ${value}`);
try {
ok(Reflect.set(b, index, value), "Reflect.set should return true");
ok(!shouldThrow, `Reflect.set should not throw`);
} catch(e) {
ok(shouldThrow, `Reflect.set throws ${e}`);
}
is(setCallbackCount, (index < minLen) ? 0 : 1, "setCallback count");
is(deleteCallbackCount, (oldLen > index) ? 1 : 0, "deleteCallback count");
is(b[index], shouldThrow ? oldValue : value, "property value");
is(b.length, shouldThrow ? oldLen : Math.max(oldLen, index + 1), "length of observable array");
});
// Test other property
[
["prop1", "value1"],
["prop1", "value2"],
["prop2", 5],
["prop3", []],
["prop4", {}],
].forEach(function([property, value]) {
// Initialize
let oldLen = b.length;
setCallbackCount = 0;
deleteCallbackCount = 0;
// Test
info(`setting ${property} property to ${JSON.stringify(value)}`);
try {
ok(Reflect.set(b, property, value), "Reflect.set should return true");
ok(true, `Reflect.set should not throw`);
} catch(e) {
ok(false, `Reflect.set throws ${e}`);
}
is(setCallbackCount, 0, "setCallback should not be called");
is(deleteCallbackCount, 0, "deleteCallback should be called");
is(b[property], value, "property value");
is(b.length, oldLen, "length of observable array");
});
});
add_task(function testObservableArrayExoticObjects_invalidtype() {
let m = new TestInterfaceObservableArray();
let i = m.observableArrayInterface;
ok(Array.isArray(i), "Observable array should be an array type");
is(i.length, 0, "length should be 0");
[true, "invalid"].forEach(function(value) {
SimpleTest.doesThrow(() => {
let descriptor = {configurable: true, enumerable: true, writable: true, value};
Reflect.defineProperty(i, i.length, descriptor);
}, `defining ${i.length} property with ${JSON.stringify(value)} should throw`);
SimpleTest.doesThrow(() => {
Reflect.set(i, i.length, value);
}, `setting ${i.length} property to ${JSON.stringify(value)} should throw`);
});
is(i.length, 0, "length should still be 0");
});
</script>
</body>
</html>

View file

@ -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

View file

@ -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<boolean> observableArrayBoolean;
attribute ObservableArray<object> observableArrayObject;
attribute ObservableArray<TestInterfaceObservableArray> 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();
};

View file

@ -1107,6 +1107,7 @@ if CONFIG["MOZ_DEBUG"] and CONFIG["ENABLE_TESTS"]:
"TestInterfaceJS.webidl",
"TestInterfaceJSDictionaries.webidl",
"TestInterfaceJSMaplikeSetlikeIterable.webidl",
"TestInterfaceObservableArray.webidl",
]
WEBIDL_FILES += [

View file

@ -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[] = {

View file

@ -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> 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> transactionManager(mTransactionManager);
DebugOnly<nsresult> 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<AutoPlaceholderBatch> 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<Selection>(*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(),

View file

@ -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<Selection> mSelection;
nsTArray<OwningNonNull<Selection>> mRetiredSelections;
nsCOMPtr<nsIPrincipal> 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<EditorBase> mEditorBase;
ScrollSelectionIntoView mScrollSelectionIntoView;
const OwningNonNull<EditorBase> 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:

View file

@ -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<nsresult> 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);

View file

@ -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,

View file

@ -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<const nsRange> 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<char16_t> 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<nsDOMAttributeMap> 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);

View file

@ -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<TypeInState> mTypeInState;

View file

@ -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> 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),

View file

@ -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<Element> resizedElement(mResizedObject);
RefPtr<nsStyledElement> resizedStyleElement =
nsStyledElement::FromNodeOrNull(mResizedObject);

View file

@ -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);

View file

@ -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 <br> 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(

View file

@ -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;

View file

@ -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");

View file

@ -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)) {

View file

@ -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 {

View file

@ -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);

View file

@ -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_);

View file

@ -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
}

View file

@ -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
}

View file

@ -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"
},

View file

@ -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',

View file

@ -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);

View file

@ -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);

View file

@ -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);
}
}
// ========================================================================

View file

@ -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");

View file

@ -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");

View file

@ -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);
}
}

View file

@ -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 = <D C> <B A>
// rhs = <H G> <F E>
@ -1723,7 +1744,7 @@ void MacroAssembler::mulInt64x2(FloatRegister lhs, FloatRegister rhs,
vpaddq(Operand(temp), temp2, temp2); // temp2 = <DG+CH> <BE+AF>
vpsllq(Imm32(32), temp2, temp2); // temp2 = <(DG+CH)_low 0>
// <(BE+AF)_low 0>
vpmuludq(rhs, dest, dest); // dest = <CG_high CG_low>
vpmuludq(rhs, lhs, dest); // dest = <CG_high CG_low>
// <AE_high AE_low>
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,

View file

@ -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]);

View file

@ -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);
}
// ========================================================================

View file

@ -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) {

View file

@ -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) {

View file

@ -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):

View file

@ -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"

View file

@ -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,

View file

@ -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; }

Some files were not shown because too many files have changed in this diff Show more