forked from mirrors/gecko-dev
Merge autoland to mozilla-central. a=merge
This commit is contained in:
commit
c3894603d8
192 changed files with 8067 additions and 2538 deletions
|
|
@ -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)
|
||||
);
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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__);
|
||||
/******/
|
||||
/******/ })()
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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})`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ const ContentProcessTargetActor = TargetActorMixin(
|
|||
if (this.isDestroyed()) {
|
||||
return;
|
||||
}
|
||||
Resources.unwatchAllTargetResources(this);
|
||||
Resources.unwatchAllResources(this);
|
||||
|
||||
Actor.prototype.destroy.call(this);
|
||||
|
||||
|
|
|
|||
|
|
@ -727,7 +727,7 @@ const windowGlobalTargetPrototype = {
|
|||
|
||||
Actor.prototype.destroy.call(this);
|
||||
TargetActorRegistry.unregisterTargetActor(this);
|
||||
Resources.unwatchAllTargetResources(this);
|
||||
Resources.unwatchAllResources(this);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ DIRS += [
|
|||
"inspected-window",
|
||||
"inspector",
|
||||
"resource",
|
||||
"root-resource",
|
||||
"script",
|
||||
"target",
|
||||
"target-configuration",
|
||||
|
|
|
|||
7
devtools/shared/commands/root-resource/moz.build
Normal file
7
devtools/shared/commands/root-resource/moz.build
Normal 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",
|
||||
)
|
||||
330
devtools/shared/commands/root-resource/root-resource-command.js
Normal file
330
devtools/shared/commands/root-resource/root-resource-command.js
Normal 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;
|
||||
|
|
@ -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"),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 ||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
%}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
366
dom/bindings/ObservableArrayProxyHandler.cpp
Normal file
366
dom/bindings/ObservableArrayProxyHandler.cpp
Normal 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
|
||||
108
dom/bindings/ObservableArrayProxyHandler.h
Normal file
108
dom/bindings/ObservableArrayProxyHandler.h
Normal 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 */
|
||||
64
dom/bindings/ProxyHandlerUtils.h
Normal file
64
dom/bindings/ProxyHandlerUtils.h
Normal 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 */
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
302
dom/bindings/parser/tests/test_observableArray.py
Normal file
302
dom/bindings/parser/tests/test_observableArray.py
Normal 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")
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
225
dom/bindings/test/TestInterfaceObservableArray.cpp
Normal file
225
dom/bindings/test/TestInterfaceObservableArray.cpp
Normal 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
|
||||
115
dom/bindings/test/TestInterfaceObservableArray.h
Normal file
115
dom/bindings/test/TestInterfaceObservableArray.h
Normal 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
|
||||
|
|
@ -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]
|
||||
|
|
|
|||
523
dom/bindings/test/test_observablearray.html
Normal file
523
dom/bindings/test/test_observablearray.html
Normal 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>
|
||||
376
dom/bindings/test/test_observablearray_helper.html
Normal file
376
dom/bindings/test/test_observablearray_helper.html
Normal 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>
|
||||
859
dom/bindings/test/test_observablearray_proxyhandler.html
Normal file
859
dom/bindings/test/test_observablearray_proxyhandler.html
Normal 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>
|
||||
|
|
@ -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
|
||||
|
|
|
|||
66
dom/webidl/TestInterfaceObservableArray.webidl
Normal file
66
dom/webidl/TestInterfaceObservableArray.webidl
Normal 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();
|
||||
};
|
||||
|
|
@ -1107,6 +1107,7 @@ if CONFIG["MOZ_DEBUG"] and CONFIG["ENABLE_TESTS"]:
|
|||
"TestInterfaceJS.webidl",
|
||||
"TestInterfaceJSDictionaries.webidl",
|
||||
"TestInterfaceJSMaplikeSetlikeIterable.webidl",
|
||||
"TestInterfaceObservableArray.webidl",
|
||||
]
|
||||
|
||||
WEBIDL_FILES += [
|
||||
|
|
|
|||
|
|
@ -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[] = {
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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_);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in a new issue