author | Noemi Erli <nerli@mozilla.com> |
Fri, 01 Jun 2018 00:51:37 +0300 | |
changeset 420724 | 42880a726964a0bd66e2f636931e8322eae86ef7 |
parent 420659 | 0dc7bfc7b0c1de8d390cea05f0248fbadeb4d5fc (current diff) |
parent 420723 | 833f1e224c8816a588d2416775bc43ed005c3e5b (diff) |
child 420779 | f364ab92b59ebeafe26ef6d579328c35a4bb99a0 |
child 420817 | 731cfcb5e07ca52408c0b156adea25500bf9dc26 |
push id | 34077 |
push user | nerli@mozilla.com |
push date | Thu, 31 May 2018 21:51:59 +0000 |
treeherder | mozilla-central@42880a726964 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 62.0a1 |
first release with | nightly linux32
42880a726964
/
62.0a1
/
20180531220139
/
files
nightly linux64
42880a726964
/
62.0a1
/
20180531220139
/
files
nightly mac
42880a726964
/
62.0a1
/
20180531220139
/
files
nightly win32
42880a726964
/
62.0a1
/
20180531220139
/
files
nightly win64
42880a726964
/
62.0a1
/
20180531220139
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
62.0a1
/
20180531220139
/
pushlog to previous
nightly linux64
62.0a1
/
20180531220139
/
pushlog to previous
nightly mac
62.0a1
/
20180531220139
/
pushlog to previous
nightly win32
62.0a1
/
20180531220139
/
pushlog to previous
nightly win64
62.0a1
/
20180531220139
/
pushlog to previous
|
--- a/Cargo.lock +++ b/Cargo.lock @@ -2356,25 +2356,29 @@ dependencies = [ "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "webrender_bindings" version = "0.1.0" dependencies = [ "app_units 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "dwrote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "gleam 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "nsstring 0.1.0", "rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "webrender 0.57.2", ] [[package]] name = "which" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [
--- a/caps/nsScriptSecurityManager.cpp +++ b/caps/nsScriptSecurityManager.cpp @@ -1248,25 +1248,16 @@ nsScriptSecurityManager::GetDocShellCode nsIPrincipal** aPrincipal) { nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(aURI, nsDocShell::Cast(aDocShell)->GetOriginAttributes()); prin.forget(aPrincipal); return *aPrincipal ? NS_OK : NS_ERROR_FAILURE; } -// static -nsIPrincipal* -nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj) -{ - JSCompartment *compartment = js::GetObjectCompartment(aObj); - JSPrincipals *principals = JS_GetCompartmentPrincipals(compartment); - return nsJSPrincipals::get(principals); -} - NS_IMETHODIMP nsScriptSecurityManager::CanCreateWrapper(JSContext *cx, const nsIID &aIID, nsISupports *aObj, nsIClassInfo *aClassInfo) { // XXX Special case for Exception ?
--- a/caps/nsScriptSecurityManager.h +++ b/caps/nsScriptSecurityManager.h @@ -88,20 +88,16 @@ private: // Decides, based on CSP, whether or not eval() and stuff can be executed. static bool ContentSecurityPolicyPermitsJSAction(JSContext *cx); static bool JSPrincipalsSubsume(JSPrincipals *first, JSPrincipals *second); - // Returns null if a principal cannot be found; generally callers - // should error out at that point. - static nsIPrincipal* doGetObjectPrincipal(JSObject* obj); - nsresult Init(); nsresult InitPrefs(); inline void ScriptSecurityPrefChanged();
--- a/devtools/client/debugger/new/README.mozilla +++ b/devtools/client/debugger/new/README.mozilla @@ -1,13 +1,13 @@ This is the debugger.html project output. See https://github.com/devtools-html/debugger.html -Version 60 +Version 61 -Comparison: https://github.com/devtools-html/debugger.html/compare/release-59...release-60 +Comparison: https://github.com/devtools-html/debugger.html/compare/release-60...release-61 Packages: - babel-plugin-transform-es2015-modules-commonjs @6.26.2 - babel-preset-react @6.24.1 - react @16.2.0 - react-dom @16.2.0 - webpack @3.11.0
--- a/devtools/client/debugger/new/dist/debugger.css +++ b/devtools/client/debugger/new/dist/debugger.css @@ -2112,16 +2112,20 @@ menuseparator { color: var(--string-color); border-width: 1px; border-style: solid; border-radius: 2px; font-size: 0.8em; padding: 0 2px; } +.objectBox-node.clickable { + cursor: pointer; +} + /******************************************************************************/ .objectBox-event, .objectBox-eventLog, .objectBox-regexp, .objectBox-object { color: var(--object-color); white-space: pre-wrap; @@ -2218,16 +2222,17 @@ menuseparator { button.open-inspector { mask: url("chrome://devtools/skin/images/devtools-reps/open-inspector.svg") no-repeat; display: inline-block; background-color: var(--comment-node-color); height: 16px; margin-left: 0.25em; vertical-align: middle; + cursor: pointer; } .objectBox-node:hover .open-inspector, .objectBox-textNode:hover .open-inspector, .open-inspector:hover { background-color: var(--theme-highlight-blue); }
--- a/devtools/client/debugger/new/dist/parser-worker.js +++ b/devtools/client/debugger/new/dist/parser-worker.js @@ -1217,17 +1217,17 @@ function isAwaitExpression(path) { } function isYieldExpression(path) { const { node, parent } = path; return t.isYieldExpression(node) || t.isYieldExpression(parent.init) || t.isYieldExpression(parent); } function isObjectShorthand(parent) { - return t.isObjectProperty(parent) && parent.key.start == parent.value.start && parent.key.loc.identifierName === parent.value.loc.identifierName; + return t.isObjectProperty(parent) && parent.value && parent.key.start == parent.value.start && parent.key.loc.identifierName === parent.value.loc.identifierName; } function getObjectExpressionValue(node) { const { value } = node; if (t.isIdentifier(value)) { return value.name; }
--- a/devtools/client/debugger/new/dist/vendors.js +++ b/devtools/client/debugger/new/dist/vendors.js @@ -8169,17 +8169,17 @@ var _Svg2 = _interopRequireDefault(_Svg) function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } // We cannot directly export literals containing special characters // (eg. "my-module/Test") which is why they are nested in "vendored". // The keys of the vendored object should match the module names // !!! Should remain synchronized with .babel/transform-mc.js !!! -const vendored = { +const vendored = exports.vendored = { classnames: _classnames2.default, "devtools-components": devtoolsComponents, "devtools-config": devtoolsConfig, "devtools-contextmenu": devtoolsContextmenu, "devtools-environment": devtoolsEnvironment, "devtools-modules": devtoolsModules, "devtools-splitter": _devtoolsSplitter2.default, "devtools-utils": devtoolsUtils, @@ -8205,17 +8205,16 @@ const vendored = { * same way: * - always with destructuring (import { a } from "modA";) * - always without destructuring (import modB from "modB") * * Both are fine, but cannot be mixed for the same module. */ // Modules imported with destructuring -exports.vendored = vendored; /***/ }), /***/ 4: /***/ (function(module, exports) { module.exports = __WEBPACK_EXTERNAL_MODULE_4__;
--- a/devtools/client/debugger/new/src/actions/pause/extra.js +++ b/devtools/client/debugger/new/src/actions/pause/extra.js @@ -86,15 +86,15 @@ function getExtra(expression, result) { dispatch, getState, client, sourceMaps }) => { const selectedFrame = (0, _selectors.getSelectedFrame)(getState()); if (!selectedFrame) { - return; + return {}; } const extra = await getExtraProps(getState, expression, result, expr => client.evaluateInFrame(expr, selectedFrame.id)); return extra; }; } \ No newline at end of file
--- a/devtools/client/debugger/new/src/client/index.js +++ b/devtools/client/debugger/new/src/client/index.js @@ -1,14 +1,14 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.onConnect = undefined; +exports.onConnect = onConnect; var _firefox = require("./firefox"); var firefox = _interopRequireWildcard(_firefox); var _prefs = require("../utils/prefs"); var _dbg = require("../utils/dbg"); @@ -63,11 +63,9 @@ async function onConnect(connection, { }); (0, _bootstrap.bootstrapApp)(store); return { store, actions, selectors, client: commands }; -} - -exports.onConnect = onConnect; \ No newline at end of file +} \ No newline at end of file
--- a/devtools/client/debugger/new/src/components/Editor/Preview/Popup.js +++ b/devtools/client/debugger/new/src/components/Editor/Preview/Popup.js @@ -232,17 +232,17 @@ class Popup extends _react.Component { let header = null; if ((0, _preview.isImmutable)(this.getObjectProperties())) { header = this.renderImmutable(extra.immutable); roots = roots.filter(r => r.type != NODE_TYPES.PROTOTYPE); } - if ((0, _preview.isReactComponent)(this.getObjectProperties())) { + if (extra.react && (0, _preview.isReactComponent)(this.getObjectProperties())) { header = this.renderReact(extra.react); roots = roots.filter(r => ["state", "props"].includes(r.name)); } return _react2.default.createElement("div", { className: "preview-popup" }, header, this.renderObjectInspector(roots)); }
--- a/devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTree.js +++ b/devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTree.js @@ -119,157 +119,166 @@ class SourcesTree extends _react.Compone debuggeeUrl, projectRoot, uncollapsedTree, sourceTree })); } } + // NOTE: we get the source from sources because item.contents is cached + getSource(item) { + return this.props.sources.get(item.contents.id); + } + + isEmpty() { + const { + sourceTree + } = this.state; + return sourceTree.contents.length === 0; + } + renderItemName(name) { const hosts = { "ng://": "Angular", "webpack://": "Webpack", "moz-extension://": L10N.getStr("extensionsText") }; return hosts[name] || name; } renderEmptyElement(message) { return _react2.default.createElement("div", { + key: "empty", className: "no-sources-message" }, message); } - render() { + renderProjectRootHeader() { const { - expanded, projectRoot } = this.props; const { - focusedItem, - highlightItems, - listItems, - parentMap, sourceTree } = this.state; - const onExpand = (item, expandedState) => { - this.props.setExpandedState(expandedState); - }; - - const onCollapse = (item, expandedState) => { - this.props.setExpandedState(expandedState); - }; - - const isEmpty = sourceTree.contents.length === 0; - const isCustomRoot = projectRoot !== ""; - - let roots = () => sourceTree.contents; - - let clearProjectRootButton = null; // The "sourceTree.contents[0]" check ensures that there are contents - // A custom root with no existing sources will be ignored - - if (isCustomRoot) { - const sourceContents = sourceTree.contents[0]; - let rootLabel = projectRoot.split("/").pop(); + if (!projectRoot) { + return null; + } - roots = () => sourceContents.contents; - - if (sourceContents && sourceContents.name !== rootLabel) { - rootLabel = sourceContents.contents[0].name; - - roots = () => sourceContents.contents[0].contents; - } + const sourceContents = sourceTree.contents[0]; + let rootLabel = projectRoot.split("/").pop(); - clearProjectRootButton = _react2.default.createElement("button", { - className: "sources-clear-root", - onClick: () => this.props.clearProjectDirectoryRoot(), - title: L10N.getStr("removeDirectoryRoot.label") - }, _react2.default.createElement(_Svg2.default, { - name: "home" - }), _react2.default.createElement(_Svg2.default, { - name: "breadcrumb", - "class": true - }), _react2.default.createElement("span", { - className: "sources-clear-root-label" - }, rootLabel)); + if (sourceContents && sourceContents.name !== rootLabel) { + rootLabel = sourceContents.contents[0].name; } - if (isEmpty && !isCustomRoot) { - return this.renderEmptyElement(L10N.getStr("sources.noSourcesAvailable")); - } + return _react2.default.createElement("div", { + key: "root", + className: "sources-clear-root-container" + }, _react2.default.createElement("button", { + className: "sources-clear-root", + onClick: () => this.props.clearProjectDirectoryRoot(), + title: L10N.getStr("removeDirectoryRoot.label") + }, _react2.default.createElement(_Svg2.default, { + name: "home" + }), _react2.default.createElement(_Svg2.default, { + name: "breadcrumb", + "class": true + }), _react2.default.createElement("span", { + className: "sources-clear-root-label" + }, rootLabel))); + } + renderTree() { + const { + expanded + } = this.props; + const { + highlightItems, + listItems, + parentMap + } = this.state; const treeProps = { autoExpandAll: false, autoExpandDepth: expanded ? 0 : 1, expanded, getChildren: item => (0, _sourcesTree.nodeHasChildren)(item) ? item.contents : [], getParent: item => parentMap.get(item), getPath: this.getPath, - getRoots: roots, + getRoots: this.getRoots, highlightItems, itemHeight: 21, - key: isEmpty ? "empty" : "full", + key: this.isEmpty() ? "empty" : "full", listItems, - onCollapse, - onExpand, + onCollapse: this.onCollapse, + onExpand: this.onExpand, onFocus: this.focusItem, renderItem: this.renderItem }; - - const tree = _react2.default.createElement(_ManagedTree2.default, treeProps); + return _react2.default.createElement(_ManagedTree2.default, treeProps); + } - const onKeyDown = e => { - if (e.keyCode === 13 && focusedItem) { - this.selectItem(focusedItem); - } - }; - + renderPane(...children) { + const { + projectRoot + } = this.props; return _react2.default.createElement("div", { + key: "pane", className: (0, _classnames2.default)("sources-pane", { - "sources-list-custom-root": isCustomRoot + "sources-list-custom-root": projectRoot }) - }, isCustomRoot ? _react2.default.createElement("div", { - className: "sources-clear-root-container" - }, clearProjectRootButton) : null, isEmpty ? this.renderEmptyElement(L10N.getStr("sources.noSourcesAvailableRoot")) : _react2.default.createElement("div", { + }, children); + } + + render() { + const { + projectRoot + } = this.props; + + if (this.isEmpty()) { + if (projectRoot) { + return this.renderPane(this.renderProjectRootHeader(), this.renderEmptyElement(L10N.getStr("sources.noSourcesAvailableRoot"))); + } + + return this.renderPane(this.renderEmptyElement(L10N.getStr("sources.noSourcesAvailable"))); + } + + return this.renderPane(this.renderProjectRootHeader(), _react2.default.createElement("div", { + key: "tree", className: "sources-list", - onKeyDown: onKeyDown - }, tree)); + onKeyDown: this.onKeyDown + }, this.renderTree())); } } var _initialiseProps = function () { this.focusItem = item => { this.setState({ focusedItem: item }); }; this.selectItem = item => { - if (!(0, _sourcesTree.nodeHasChildren)(item)) { - this.props.selectLocation({ - sourceId: item.contents.get("id") - }); + if (!(0, _sourcesTree.isDirectory)(item)) { + this.props.selectSource(item.contents.id); } }; this.getPath = item => { - const { - sources - } = this.props; - const obj = item.contents.get && item.contents.get("id"); - let blackBoxedPart = ""; + const path = `${item.path}/${item.name}`; - if (typeof obj !== "undefined" && sources.has(obj) && sources.get(obj).get("isBlackBoxed")) { - blackBoxedPart = "update"; + if ((0, _sourcesTree.isDirectory)(item)) { + return path; } - return `${item.path}/${item.name}/${blackBoxedPart}`; + const source = this.getSource(item); + const blackBoxedPart = source.isBlackBoxed ? ":blackboxed" : ""; + return `${path}${blackBoxedPart}`; }; this.getIcon = (sources, item, depth) => { const { debuggeeUrl, projectRoot } = this.props; @@ -290,48 +299,45 @@ var _initialiseProps = function () { if (depth === 0 && projectRoot === "") { return _react2.default.createElement("img", { className: (0, _classnames2.default)("domain", { debuggee: debuggeeUrl && debuggeeUrl.includes(item.name) }) }); } - if (!(0, _sourcesTree.nodeHasChildren)(item)) { - const obj = item.contents.get("id"); - const source = sources.get(obj); - const className = (0, _classnames2.default)((0, _source.getSourceClassnames)(source), "source-icon"); + if ((0, _sourcesTree.isDirectory)(item)) { return _react2.default.createElement("img", { - className: className + className: "folder" }); } + const source = this.getSource(item); return _react2.default.createElement("img", { - className: "folder" + className: (0, _classnames2.default)((0, _source.getSourceClassnames)(source), "source-icon") }); }; this.onContextMenu = (event, item) => { const copySourceUri2Label = L10N.getStr("copySourceUri2"); const copySourceUri2Key = L10N.getStr("copySourceUri2.accesskey"); const setDirectoryRootLabel = L10N.getStr("setDirectoryRoot.label"); const setDirectoryRootKey = L10N.getStr("setDirectoryRoot.accesskey"); const removeDirectoryRootLabel = L10N.getStr("removeDirectoryRoot.label"); event.stopPropagation(); event.preventDefault(); const menuOptions = []; if (!(0, _sourcesTree.isDirectory)(item)) { - const source = item.contents.get("url"); const copySourceUri2 = { id: "node-menu-copy-source", label: copySourceUri2Label, accesskey: copySourceUri2Key, disabled: false, - click: () => (0, _clipboard.copyToTheClipboard)(source) + click: () => (0, _clipboard.copyToTheClipboard)(item.contents.url) }; menuOptions.push(copySourceUri2); } if ((0, _sourcesTree.isDirectory)(item) && _prefs.features.root) { const { path } = item; @@ -355,16 +361,34 @@ var _initialiseProps = function () { click: () => this.props.setProjectDirectoryRoot(path) }); } } (0, _devtoolsContextmenu.showMenu)(event, menuOptions); }; + this.onExpand = (item, expandedState) => { + this.props.setExpandedState(expandedState); + }; + + this.onCollapse = (item, expandedState) => { + this.props.setExpandedState(expandedState); + }; + + this.onKeyDown = e => { + const { + focusedItem + } = this.state; + + if (e.keyCode === 13 && focusedItem) { + this.selectItem(focusedItem); + } + }; + this.renderItem = (item, depth, focused, _, expanded, { setExpanded }) => { const arrow = (0, _sourcesTree.nodeHasChildren)(item) ? _react2.default.createElement("img", { className: (0, _classnames2.default)("arrow", { expanded: expanded }) }) : _react2.default.createElement("i", { @@ -388,28 +412,50 @@ var _initialiseProps = function () { this.selectItem(item); } }, onContextMenu: e => this.onContextMenu(e, item) }, arrow, icon, _react2.default.createElement("span", { className: "label" }, " ", this.renderItemName(item.name), " ")); }; + + this.getRoots = () => { + const { + projectRoot + } = this.props; + const { + sourceTree + } = this.state; + const sourceContents = sourceTree.contents[0]; + const rootLabel = projectRoot.split("/").pop(); // The "sourceTree.contents[0]" check ensures that there are contents + // A custom root with no existing sources will be ignored + + if (projectRoot) { + if (sourceContents && sourceContents.name !== rootLabel) { + return sourceContents.contents[0].contents; + } + + return sourceContents.contents; + } + + return sourceTree.contents; + }; }; const mapStateToProps = state => { return { shownSource: (0, _selectors.getShownSource)(state), selectedSource: (0, _selectors.getSelectedSource)(state), debuggeeUrl: (0, _selectors.getDebuggeeUrl)(state), expanded: (0, _selectors.getExpandedState)(state), projectRoot: (0, _selectors.getProjectDirectoryRoot)(state), sources: (0, _selectors.getSources)(state) }; }; const actionCreators = { setExpandedState: _sourceTree.setExpandedState, - selectLocation: _sources.selectLocation, + selectSource: _sources.selectSource, setProjectDirectoryRoot: _ui.setProjectDirectoryRoot, clearProjectDirectoryRoot: _ui.clearProjectDirectoryRoot }; exports.default = (0, _reactRedux.connect)(mapStateToProps, actionCreators)(SourcesTree); \ No newline at end of file
--- a/devtools/client/debugger/new/src/utils/editor/source-documents.js +++ b/devtools/client/debugger/new/src/utils/editor/source-documents.js @@ -1,14 +1,24 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.showLoading = exports.showErrorMessage = exports.showSourceText = exports.clearEditor = exports.updateDocument = exports.updateLineNumberFormat = exports.clearDocuments = exports.removeDocument = exports.hasDocument = exports.setDocument = exports.getDocument = undefined; +exports.getDocument = getDocument; +exports.hasDocument = hasDocument; +exports.setDocument = setDocument; +exports.removeDocument = removeDocument; +exports.clearDocuments = clearDocuments; +exports.updateLineNumberFormat = updateLineNumberFormat; +exports.updateDocument = updateDocument; +exports.clearEditor = clearEditor; +exports.showLoading = showLoading; +exports.showErrorMessage = showErrorMessage; +exports.showSourceText = showSourceText; var _source = require("../source"); var _wasm = require("../wasm"); var _ui = require("../ui"); var _sourceEditor = require("devtools/client/sourceeditor/editor"); @@ -163,21 +173,9 @@ function showSourceText(editor, source, } const doc = editor.createDocument(); setDocument(source.id, doc); editor.replaceDocument(doc); setEditorText(editor, source); editor.setMode((0, _source.getMode)(source, symbols)); updateLineNumberFormat(editor, source.id); -} - -exports.getDocument = getDocument; -exports.setDocument = setDocument; -exports.hasDocument = hasDocument; -exports.removeDocument = removeDocument; -exports.clearDocuments = clearDocuments; -exports.updateLineNumberFormat = updateLineNumberFormat; -exports.updateDocument = updateDocument; -exports.clearEditor = clearEditor; -exports.showSourceText = showSourceText; -exports.showErrorMessage = showErrorMessage; -exports.showLoading = showLoading; \ No newline at end of file +} \ No newline at end of file
--- a/devtools/client/debugger/new/src/utils/path.js +++ b/devtools/client/debugger/new/src/utils/path.js @@ -1,13 +1,18 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); +exports.basename = basename; +exports.dirname = dirname; +exports.isURL = isURL; +exports.isAbsolute = isAbsolute; +exports.join = join; /* 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 basename(path) { return path.split("/").pop(); } @@ -21,15 +26,9 @@ function isURL(str) { } function isAbsolute(str) { return str[0] === "/"; } function join(base, dir) { return `${base}/${dir}`; -} - -exports.basename = basename; -exports.dirname = dirname; -exports.isURL = isURL; -exports.isAbsolute = isAbsolute; -exports.join = join; \ No newline at end of file +} \ No newline at end of file
--- a/devtools/client/debugger/new/src/utils/result-list.js +++ b/devtools/client/debugger/new/src/utils/result-list.js @@ -1,14 +1,14 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.scrollList = undefined; +exports.scrollList = scrollList; var _devtoolsEnvironment = require("devtools/client/debugger/new/dist/vendors").vendored["devtools-environment"]; var _Modal = require("../components/shared/Modal"); /* 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/>. */ @@ -48,11 +48,9 @@ function chromeScrollList(elem, index) { const resultsHeight = resultsEl.clientHeight; const itemHeight = resultsEl.children[0].clientHeight; const numVisible = resultsHeight / itemHeight; const positionsToScroll = index - numVisible + 1; const itemOffset = resultsHeight % itemHeight; const scroll = positionsToScroll * (itemHeight + 2) + itemOffset; resultsEl.scrollTop = Math.max(0, scroll); -} - -exports.scrollList = scrollList; \ No newline at end of file +} \ No newline at end of file
--- a/devtools/client/debugger/new/src/utils/source.js +++ b/devtools/client/debugger/new/src/utils/source.js @@ -421,27 +421,22 @@ function isLoaded(source) { return source.get("loadedState") === "loaded"; } function isLoading(source) { return source.get("loadedState") === "loading"; } function getTextAtPosition(source, location) { - if (!source || !source.text) { + if (!source || !source.text || source.isWasm) { return ""; } const line = location.line; const column = location.column || 0; - - if (source.isWasm) { - return ""; - } - const lineText = source.text.split("\n")[line - 1]; if (!lineText) { return ""; } return lineText.slice(column, column + 100).trim(); } @@ -461,10 +456,10 @@ function getSourceClassnames(source, sou if (isPretty(source)) { return "prettyPrint"; } if (source.isBlackBoxed) { return "blackBox"; } - return sourceTypes[(0, _sourcesTree.getExtension)(source.url)] || defaultClassName; + return sourceTypes[(0, _sourcesTree.getFileExtension)(source.url)] || defaultClassName; } \ No newline at end of file
--- a/devtools/client/debugger/new/src/utils/sources-tree/addToTree.js +++ b/devtools/client/debugger/new/src/utils/sources-tree/addToTree.js @@ -124,17 +124,17 @@ function addSourceToNode(node, url, sour } /** * @memberof utils/sources-tree * @static */ function addToTree(tree, source, debuggeeUrl, projectRoot) { - const url = (0, _getURL.getURL)(source.get("url"), debuggeeUrl); + const url = (0, _getURL.getURL)(source.get ? source.get("url") : source.url, debuggeeUrl); const debuggeeHost = (0, _treeOrder.getDomain)(debuggeeUrl); if ((0, _utils.isInvalidUrl)(url, source) || !isUnderRoot(url, projectRoot)) { return; } const finalNode = traverseTree(url, tree, debuggeeHost, projectRoot); finalNode.contents = addSourceToNode(finalNode, url, source);
--- a/devtools/client/debugger/new/src/utils/sources-tree/index.js +++ b/devtools/client/debugger/new/src/utils/sources-tree/index.js @@ -97,16 +97,22 @@ Object.defineProperty(exports, "createNo } }); Object.defineProperty(exports, "createParentMap", { enumerable: true, get: function () { return _utils.createParentMap; } }); +Object.defineProperty(exports, "getFileExtension", { + enumerable: true, + get: function () { + return _utils.getFileExtension; + } +}); Object.defineProperty(exports, "getRelativePath", { enumerable: true, get: function () { return _utils.getRelativePath; } }); Object.defineProperty(exports, "isDirectory", { enumerable: true, @@ -126,15 +132,9 @@ Object.defineProperty(exports, "isNotJav return _utils.isNotJavaScript; } }); Object.defineProperty(exports, "nodeHasChildren", { enumerable: true, get: function () { return _utils.nodeHasChildren; } -}); -Object.defineProperty(exports, "getExtension", { - enumerable: true, - get: function () { - return _utils.getExtension; - } }); \ No newline at end of file
--- a/devtools/client/debugger/new/src/utils/sources-tree/utils.js +++ b/devtools/client/debugger/new/src/utils/sources-tree/utils.js @@ -1,17 +1,17 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.nodeHasChildren = nodeHasChildren; exports.isExactUrlMatch = isExactUrlMatch; exports.isDirectory = isDirectory; -exports.getExtension = getExtension; +exports.getFileExtension = getFileExtension; exports.isNotJavaScript = isNotJavaScript; exports.isInvalidUrl = isInvalidUrl; exports.partIsFile = partIsFile; exports.createNode = createNode; exports.createParentMap = createParentMap; exports.getRelativePath = getRelativePath; var _url = require("devtools/client/debugger/new/dist/vendors").vendored["url"]; @@ -42,32 +42,32 @@ function isExactUrlMatch(pathPart, debug function isDirectory(url) { const parts = url.path.split("/").filter(p => p !== ""); // Assume that all urls point to files except when they end with '/' // Or directory node has children return (parts.length === 0 || url.path.slice(-1) === "/" || nodeHasChildren(url)) && url.name != "(index)"; } -function getExtension(url = "") { +function getFileExtension(url = "") { const parsedUrl = (0, _url.parse)(url).pathname; if (!parsedUrl) { return ""; } return parsedUrl.split(".").pop(); } function isNotJavaScript(source) { - return ["css", "svg", "png"].includes(getExtension(source.url)); + return ["css", "svg", "png"].includes(getFileExtension(source.url)); } function isInvalidUrl(url, source) { - return IGNORED_URLS.indexOf(url) != -1 || !source.get("url") || !url.group || (0, _source.isPretty)(source) || isNotJavaScript(source); + return IGNORED_URLS.indexOf(url) != -1 || !(source.get ? source.get("url") : source.url) || !url.group || (0, _source.isPretty)(source) || isNotJavaScript(source); } function partIsFile(index, parts, url) { const isLastPart = index === parts.length - 1; return !isDirectory(url) && isLastPart; } function createNode(name, path, contents) {
--- a/devtools/client/debugger/new/src/utils/task.js +++ b/devtools/client/debugger/new/src/utils/task.js @@ -10,17 +10,17 @@ Object.defineProperty(exports, "__esModu /* 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/>. */ /** * This object provides the public module functions. */ -const Task = { +const Task = exports.Task = { // XXX: Not sure if this works in all cases... async: function (task) { return function () { return Task.spawn(task, this, arguments); }; }, /** @@ -44,10 +44,9 @@ const Task = { reject(error); iterator.throw(error); }); }; callNext(undefined); }); } -}; -exports.Task = Task; \ No newline at end of file +}; \ No newline at end of file
--- a/devtools/client/debugger/new/src/utils/text.js +++ b/devtools/client/debugger/new/src/utils/text.js @@ -1,14 +1,15 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.truncateMiddleText = exports.formatKeyShortcut = undefined; +exports.formatKeyShortcut = formatKeyShortcut; +exports.truncateMiddleText = truncateMiddleText; var _devtoolsModules = require("devtools/client/debugger/new/dist/vendors").vendored["devtools-modules"]; /* 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/>. */ /** @@ -54,12 +55,9 @@ function formatKeyShortcut(shortcut) { function truncateMiddleText(sourceText, maxLength) { let truncatedText = sourceText; if (sourceText.length > maxLength) { truncatedText = `${sourceText.substring(0, Math.round(maxLength / 2) - 2)}...${sourceText.substring(sourceText.length - Math.round(maxLength / 2 - 1))}`; } return truncatedText; -} - -exports.formatKeyShortcut = formatKeyShortcut; -exports.truncateMiddleText = truncateMiddleText; \ No newline at end of file +} \ No newline at end of file
--- a/devtools/client/debugger/new/src/utils/utils.js +++ b/devtools/client/debugger/new/src/utils/utils.js @@ -1,13 +1,17 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); +exports.handleError = handleError; +exports.promisify = promisify; +exports.endTruncateStr = endTruncateStr; +exports.waitForMs = waitForMs; /* 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/>. */ /** * Utils for utils, by utils * @module utils/utils @@ -49,14 +53,9 @@ function endTruncateStr(str, size) { return `...${str.slice(str.length - size)}`; } return str; } function waitForMs(ms) { return new Promise(resolve => setTimeout(resolve, ms)); -} - -exports.handleError = handleError; -exports.promisify = promisify; -exports.endTruncateStr = endTruncateStr; -exports.waitForMs = waitForMs; \ No newline at end of file +} \ No newline at end of file
--- a/devtools/client/debugger/new/src/utils/wasm.js +++ b/devtools/client/debugger/new/src/utils/wasm.js @@ -1,14 +1,20 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.renderWasmText = exports.clearWasmStates = exports.wasmOffsetToLine = exports.lineToWasmOffset = exports.isWasm = exports.getWasmLineNumberFormatter = exports.getWasmText = undefined; +exports.getWasmText = getWasmText; +exports.getWasmLineNumberFormatter = getWasmLineNumberFormatter; +exports.isWasm = isWasm; +exports.lineToWasmOffset = lineToWasmOffset; +exports.wasmOffsetToLine = wasmOffsetToLine; +exports.clearWasmStates = clearWasmStates; +exports.renderWasmText = renderWasmText; var _WasmParser = require("devtools/client/shared/vendor/WasmParser"); var _WasmDis = require("devtools/client/shared/vendor/WasmDis"); /* 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/>. */ @@ -174,17 +180,9 @@ function renderWasmText(sourceId, { const MAX_LINES = 1000000; if (lines.length > MAX_LINES) { lines.splice(MAX_LINES, lines.length - MAX_LINES); lines.push(";; .... text is truncated due to the size"); } return lines; -} - -exports.getWasmText = getWasmText; -exports.getWasmLineNumberFormatter = getWasmLineNumberFormatter; -exports.isWasm = isWasm; -exports.lineToWasmOffset = lineToWasmOffset; -exports.wasmOffsetToLine = wasmOffsetToLine; -exports.clearWasmStates = clearWasmStates; -exports.renderWasmText = renderWasmText; \ No newline at end of file +} \ No newline at end of file
--- a/devtools/client/debugger/new/src/workers/parser/utils/helpers.js +++ b/devtools/client/debugger/new/src/workers/parser/utils/helpers.js @@ -51,17 +51,17 @@ function isYieldExpression(path) { const { node, parent } = path; return t.isYieldExpression(node) || t.isYieldExpression(parent.init) || t.isYieldExpression(parent); } function isObjectShorthand(parent) { - return t.isObjectProperty(parent) && parent.key.start == parent.value.start && parent.key.loc.identifierName === parent.value.loc.identifierName; + return t.isObjectProperty(parent) && parent.value && parent.key.start == parent.value.start && parent.key.loc.identifierName === parent.value.loc.identifierName; } function getObjectExpressionValue(node) { const { value } = node; if (t.isIdentifier(value)) {
--- a/devtools/client/shared/components/reps/reps.css +++ b/devtools/client/shared/components/reps/reps.css @@ -221,16 +221,20 @@ html[dir="rtl"] .tree-node img.arrow { color: var(--string-color); border-width: 1px; border-style: solid; border-radius: 2px; font-size: 0.8em; padding: 0 2px; } +.objectBox-node.clickable { + cursor: pointer; +} + /******************************************************************************/ .objectBox-event, .objectBox-eventLog, .objectBox-regexp, .objectBox-object { color: var(--object-color); white-space: pre-wrap; @@ -327,16 +331,17 @@ html[dir="rtl"] .tree-node img.arrow { button.open-inspector { mask: url("chrome://devtools/skin/images/devtools-reps/open-inspector.svg") no-repeat; display: inline-block; background-color: var(--comment-node-color); height: 16px; margin-left: 0.25em; vertical-align: middle; + cursor: pointer; } .objectBox-node:hover .open-inspector, .objectBox-textNode:hover .open-inspector, .open-inspector:hover { background-color: var(--theme-highlight-blue); }
--- a/devtools/client/shared/components/reps/reps.js +++ b/devtools/client/shared/components/reps/reps.js @@ -5675,17 +5675,18 @@ function ElementNode(props) { const baseConfig = { "data-link-actor-id": object.actor, className: "objectBox objectBox-node" }; let inspectIcon; if (isInTree) { if (onDOMNodeClick) { Object.assign(baseConfig, { - onClick: _ => onDOMNodeClick(object) + onClick: _ => onDOMNodeClick(object), + className: `${baseConfig.className} clickable` }); } if (onDOMNodeMouseOver) { Object.assign(baseConfig, { onMouseOver: _ => onDOMNodeMouseOver(object) }); }
--- a/devtools/server/actors/accessibility.js +++ b/devtools/server/actors/accessibility.js @@ -498,18 +498,25 @@ const AccessibleWalkerActor = ActorClass this.refMap.clear(); } }, /** * A helper method. Accessibility walker is assumed to have only 1 child which * is the top level document. */ - children() { - return Promise.all([this.getDocument()]); + async children() { + if (this._childrenPromise) { + return this._childrenPromise; + } + + this._childrenPromise = Promise.all([this.getDocument()]); + let children = await this._childrenPromise; + this._childrenPromise = null; + return children; }, /** * A promise for a root document accessible actor that only resolves when its * corresponding document accessible object is fully loaded. * * @return {Promise} */
--- a/docshell/base/nsPingListener.cpp +++ b/docshell/base/nsPingListener.cpp @@ -157,17 +157,17 @@ SendPing(void* aClosure, nsIContent* aCo MOZ_ASSERT(NS_SUCCEEDED(rv)); } nsCOMPtr<nsIScriptSecurityManager> sm = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); if (sm && info->referrer) { bool referrerIsSecure; - uint32_t flags = nsIProtocolHandler::URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT; + uint32_t flags = nsIProtocolHandler::URI_IS_POTENTIALLY_TRUSTWORTHY; rv = NS_URIChainHasFlags(info->referrer, flags, &referrerIsSecure); // Default to sending less data if NS_URIChainHasFlags() fails. referrerIsSecure = NS_FAILED(rv) || referrerIsSecure; bool sameOrigin = NS_SUCCEEDED(sm->CheckSameOriginURI(info->referrer, aURI, false));
--- a/dom/base/DocGroup.cpp +++ b/dom/base/DocGroup.cpp @@ -179,10 +179,22 @@ DocGroup::MoveSignalSlotListTo(nsTArray< aDest.SetCapacity(aDest.Length() + mSignalSlotList.Length()); for (RefPtr<HTMLSlotElement>& slot : mSignalSlotList) { slot->RemovedFromSignalSlotList(); aDest.AppendElement(Move(slot)); } mSignalSlotList.Clear(); } +bool +DocGroup::IsActive() const +{ + for (nsIDocument* doc : mDocuments) { + if (doc->IsCurrentActiveDocument()) { + return true; + } + } + + return false; +} + } }
--- a/dom/base/DocGroup.h +++ b/dom/base/DocGroup.h @@ -117,16 +117,19 @@ public: // microtask. void SignalSlotChange(HTMLSlotElement& aSlot); void MoveSignalSlotListTo(nsTArray<RefPtr<HTMLSlotElement>>& aDest); // List of DocGroups that has non-empty signal slot list. static AutoTArray<RefPtr<DocGroup>, 2>* sPendingDocGroups; + // Returns true if any of its documents are active but not in the bfcache. + bool IsActive() const; + private: DocGroup(TabGroup* aTabGroup, const nsACString& aKey); ~DocGroup(); nsCString mKey; RefPtr<TabGroup> mTabGroup; nsTArray<nsIDocument*> mDocuments; RefPtr<mozilla::dom::CustomElementReactionsStack> mReactionsStack;
--- a/dom/base/TabGroup.cpp +++ b/dom/base/TabGroup.cpp @@ -301,10 +301,27 @@ TabGroup::IsBackground() const } } MOZ_ASSERT(foregrounded == mForegroundCount); #endif return mForegroundCount == 0; } +uint32_t +TabGroup::Count(bool aActiveOnly) const +{ + if (!aActiveOnly) { + return mDocGroups.Count(); + } + + uint32_t count = 0; + for (auto iter = mDocGroups.ConstIter(); !iter.Done(); iter.Next()) { + if (iter.Get()->mDocGroup->IsActive()) { + ++count; + } + } + + return count; +} + } // namespace dom } // namespace mozilla
--- a/dom/base/TabGroup.h +++ b/dom/base/TabGroup.h @@ -95,16 +95,20 @@ public: void Leave(nsPIDOMWindowOuter* aWindow); Iterator Iter() { return mDocGroups.Iter(); } + // Returns the size of the set of "similar-origin" DocGroups. To + // only consider DocGroups with at least one active document, call + // Count with 'aActiveOnly' = true + uint32_t Count(bool aActiveOnly = false) const; // Returns the nsIDocShellTreeItem with the given name, searching each of the // docShell trees which are within this TabGroup. It will pass itself as // aRequestor to each docShellTreeItem which it asks to search for the name, // and will not search the docShellTreeItem which is passed as aRequestor. // // This method is used in order to correctly namespace named windows based on // their unit of related browsing contexts.
--- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -167,17 +167,16 @@ #include "nsIScriptError.h" #include "nsIScriptGlobalObject.h" #include "nsIScriptObjectPrincipal.h" #include "nsIScriptSecurityManager.h" #include "nsIScrollable.h" #include "nsIStreamConverterService.h" #include "nsIStringBundle.h" #include "nsIURI.h" -#include "nsIURIWithPrincipal.h" #include "nsIURL.h" #include "nsIWebNavigation.h" #include "nsIWindowMediator.h" #include "nsIXPConnect.h" #include "nsJSUtils.h" #include "nsMappedAttributes.h" #include "nsNetCID.h" #include "nsNetUtil.h" @@ -3080,39 +3079,39 @@ nsContentUtils::GenerateStateKey(nsICont // static nsIPrincipal* nsContentUtils::SubjectPrincipal(JSContext* aCx) { MOZ_ASSERT(NS_IsMainThread()); // As opposed to SubjectPrincipal(), we do in fact assume that - // we're in a compartment here; anyone who calls this function - // in situations where that's not the case is doing it wrong. - JSCompartment* compartment = js::GetContextCompartment(aCx); - MOZ_ASSERT(compartment); - - JSPrincipals* principals = JS_GetCompartmentPrincipals(compartment); + // we're in a realm here; anyone who calls this function in + // situations where that's not the case is doing it wrong. + JS::Realm* realm = js::GetContextRealm(aCx); + MOZ_ASSERT(realm); + + JSPrincipals* principals = JS::GetRealmPrincipals(realm); return nsJSPrincipals::get(principals); } // static nsIPrincipal* nsContentUtils::SubjectPrincipal() { MOZ_ASSERT(IsInitialized()); MOZ_ASSERT(NS_IsMainThread()); JSContext* cx = GetCurrentJSContext(); if (!cx) { MOZ_CRASH("Accessing the Subject Principal without an AutoJSAPI on the stack is forbidden"); } - JSCompartment *compartment = js::GetContextCompartment(cx); - - // When an AutoJSAPI is instantiated, we are in a null compartment until the + JS::Realm* realm = js::GetContextRealm(cx); + + // When an AutoJSAPI is instantiated, we are in a null realm until the // first JSAutoRealm, which is kind of a purgatory as far as permissions // go. It would be nice to just hard-abort if somebody does a security check // in this purgatory zone, but that would be too fragile, since it could be // triggered by random IsCallerChrome() checks 20-levels deep. // // So we want to return _something_ here - and definitely not the System // Principal, since that would make an AutoJSAPI a very dangerous thing to // instantiate. @@ -3120,19 +3119,19 @@ nsContentUtils::SubjectPrincipal() // The natural thing to return is a null principal. Ideally, we'd return a // different null principal each time, to avoid any unexpected interactions // when the principal accidentally gets inherited somewhere. But // SubjectPrincipal doesn't return strong references, so there's no way to // sanely manage the lifetime of multiple null principals. // // So we use a singleton null principal. To avoid it being accidentally // inherited and becoming a "real" subject or object principal, we do a - // release-mode assert during compartment creation against using this - // principal on an actual global. - if (!compartment) { + // release-mode assert during realm creation against using this principal on + // an actual global. + if (!realm) { return sNullSubjectPrincipal; } return SubjectPrincipal(cx); } // static nsIPrincipal* @@ -6280,40 +6279,43 @@ nsContentUtils::GetASCIIOrigin(nsIPrinci } /* static */ nsresult nsContentUtils::GetASCIIOrigin(nsIURI* aURI, nsACString& aOrigin) { MOZ_ASSERT(aURI, "missing uri"); - // For Blob URI we have to return the origin of page using its principal. - nsCOMPtr<nsIURIWithPrincipal> uriWithPrincipal = do_QueryInterface(aURI); - if (uriWithPrincipal) { - nsCOMPtr<nsIPrincipal> principal; - uriWithPrincipal->GetPrincipal(getter_AddRefs(principal)); - - if (principal) { - nsCOMPtr<nsIURI> uri; - nsresult rv = principal->GetURI(getter_AddRefs(uri)); - NS_ENSURE_SUCCESS(rv, rv); - - if (uri && uri != aURI) { - return GetASCIIOrigin(uri, aOrigin); - } - } + bool isBlobURL = false; + nsresult rv = aURI->SchemeIs(BLOBURI_SCHEME, &isBlobURL); + NS_ENSURE_SUCCESS(rv, rv); + + // For Blob URI, the path is the URL of the owning page. + if (isBlobURL) { + nsAutoCString path; + rv = aURI->GetPathQueryRef(path); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIURI> uri; + nsresult rv = NS_NewURI(getter_AddRefs(uri), path); + if (NS_FAILED(rv)) { + aOrigin.AssignLiteral("null"); + return NS_OK; + } + + return GetASCIIOrigin(uri, aOrigin); } aOrigin.Truncate(); nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI); NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED); nsCString host; - nsresult rv = uri->GetAsciiHost(host); + rv = uri->GetAsciiHost(host); if (NS_SUCCEEDED(rv) && !host.IsEmpty()) { nsCString scheme; rv = uri->GetScheme(scheme); NS_ENSURE_SUCCESS(rv, rv); int32_t port = -1; uri->GetPort(&port);
--- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -2359,19 +2359,19 @@ nsIDocument::ResetToURI(nsIURI* aURI, } } } if (mFontFaceSet) { mFontFaceSet->RefreshStandardFontLoadPrincipal(); } - // Refresh the principal on the compartment. + // Refresh the principal on the realm. if (nsPIDOMWindowInner* win = GetInnerWindow()) { - nsGlobalWindowInner::Cast(win)->RefreshCompartmentPrincipal(); + nsGlobalWindowInner::Cast(win)->RefreshRealmPrincipal(); } } already_AddRefed<nsIPrincipal> nsIDocument::MaybeDowngradePrincipal(nsIPrincipal* aPrincipal) { if (!aPrincipal) { return nullptr; @@ -8490,16 +8490,28 @@ DispatchFullScreenChange(nsIDocument* aT /* Bubbles */ true, /* OnlyChrome */ false); } static void ClearPendingFullscreenRequests(nsIDocument* aDoc); void nsIDocument::OnPageHide(bool aPersisted, EventTarget* aDispatchStartTarget) { + if (mDocGroup && Telemetry::CanRecordExtended() && + IsTopLevelContentDocument()) { + TabGroup* tabGroup = mDocGroup->GetTabGroup(); + + if (tabGroup) { + Telemetry::Accumulate(Telemetry::ACTIVE_DOCGROUPS_PER_TABGROUP, + tabGroup->Count(true /* aActiveOnly */)); + Telemetry::Accumulate(Telemetry::TOTAL_DOCGROUPS_PER_TABGROUP, + tabGroup->Count()); + } + } + // Send out notifications that our <link> elements are detached, // but only if this is not a full unload. Element* root = GetRootElement(); if (aPersisted && root) { RefPtr<nsContentList> links = NS_GetContentList(root, kNameSpaceID_XHTML, NS_LITERAL_STRING("link"));
--- a/dom/base/nsGlobalWindowInner.cpp +++ b/dom/base/nsGlobalWindowInner.cpp @@ -3627,20 +3627,20 @@ nsGlobalWindowInner::GetChildWindow(cons { if (GetOuterWindowInternal()) { return GetOuterWindowInternal()->GetChildWindow(aName); } return nullptr; } void -nsGlobalWindowInner::RefreshCompartmentPrincipal() -{ - JS_SetCompartmentPrincipals(js::GetObjectCompartment(GetWrapperPreserveColor()), - nsJSPrincipals::get(mDoc->NodePrincipal())); +nsGlobalWindowInner::RefreshRealmPrincipal() +{ + JS::SetRealmPrincipals(js::GetNonCCWObjectRealm(GetWrapperPreserveColor()), + nsJSPrincipals::get(mDoc->NodePrincipal())); } already_AddRefed<nsIWidget> nsGlobalWindowInner::GetMainWidget() { FORWARD_TO_OUTER(GetMainWidget, (), nullptr); }
--- a/dom/base/nsGlobalWindowInner.h +++ b/dom/base/nsGlobalWindowInner.h @@ -372,17 +372,17 @@ public: bool aForceReuseInnerWindow) override; virtual void SetOpenerWindow(nsPIDOMWindowOuter* aOpener, bool aOriginalOpener) override; virtual void MaybeUpdateTouchState() override; // Inner windows only. - void RefreshCompartmentPrincipal(); + void RefreshRealmPrincipal(); // For accessing protected field mFullScreen friend class FullscreenTransitionTask; // Inner windows only. virtual void SetHasGamepadEventListener(bool aHasGamepad = true) override; void NotifyVREventListenerAdded(); bool HasUsedVR() const;
--- a/dom/base/nsGlobalWindowOuter.cpp +++ b/dom/base/nsGlobalWindowOuter.cpp @@ -1742,34 +1742,36 @@ nsGlobalWindowOuter::SetNewDocument(nsID // so all expandos and such defined on the outer window should go away. Force // all Xray wrappers to be recomputed. JS::Rooted<JSObject*> rootedObject(cx, GetWrapper()); if (!JS_RefreshCrossCompartmentWrappers(cx, rootedObject)) { return NS_ERROR_FAILURE; } // Inner windows are only reused for same-origin principals, but the principals - // don't necessarily match exactly. Update the principal on the compartment to - // match the new document. - // NB: We don't just call currentInner->RefreshCompartmentPrincipals() here + // don't necessarily match exactly. Update the principal on the realm to match + // the new document. + // NB: We don't just call currentInner->RefreshRealmPrincipals() here // because we haven't yet set its mDoc to aDocument. - JSCompartment *compartment = js::GetObjectCompartment(newInnerGlobal); + JS::Realm* realm = js::GetNonCCWObjectRealm(newInnerGlobal); #ifdef DEBUG bool sameOrigin = false; nsIPrincipal *existing = - nsJSPrincipals::get(JS_GetCompartmentPrincipals(compartment)); + nsJSPrincipals::get(JS::GetRealmPrincipals(realm)); aDocument->NodePrincipal()->Equals(existing, &sameOrigin); MOZ_ASSERT(sameOrigin); -#endif + + JSCompartment* compartment = JS::GetCompartmentForRealm(realm); MOZ_ASSERT_IF(aDocument == oldDoc, xpc::GetCompartmentPrincipal(compartment) == aDocument->NodePrincipal()); +#endif if (aDocument != oldDoc) { - JS_SetCompartmentPrincipals(compartment, - nsJSPrincipals::get(aDocument->NodePrincipal())); + JS::SetRealmPrincipals(realm, + nsJSPrincipals::get(aDocument->NodePrincipal())); // Make sure we clear out the old content XBL scope, so the new one will // get created with a principal that subsumes our new principal. xpc::ClearContentXBLScope(newInnerGlobal); } } else { if (aState) { newInnerWindow = wsh->GetInnerWindow(); newInnerGlobal = newInnerWindow->GetWrapperPreserveColor(); @@ -1975,23 +1977,23 @@ nsGlobalWindowOuter::SetNewDocument(nsID // We no longer need the old inner window. Start its destruction if // its not being reused and clear our reference. if (doomCurrentInner) { currentInner->FreeInnerObjects(); } currentInner = nullptr; // Ask the JS engine to assert that it's valid to access our DocGroup whenever - // it runs JS code for this compartment. We skip the check if this window is - // for chrome JS or an add-on. + // it runs JS code for this realm. We skip the check if this window is for + // chrome JS or an add-on. nsCOMPtr<nsIPrincipal> principal = mDoc->NodePrincipal(); if (GetDocGroup() && !nsContentUtils::IsSystemPrincipal(principal) && !BasePrincipal::Cast(principal)->AddonPolicy()) { - js::SetCompartmentValidAccessPtr(cx, newInnerGlobal, - newInnerWindow->GetDocGroup()->GetValidAccessPtr()); + js::SetRealmValidAccessPtr(cx, newInnerGlobal, + newInnerWindow->GetDocGroup()->GetValidAccessPtr()); } kungFuDeathGrip->DidInitializeContext(); // We wait to fire the debugger hook until the window is all set up and hooked // up with the outer. See bug 969156. if (createdInnerWindow) { nsContentUtils::AddScriptRunner(
--- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -2558,31 +2558,31 @@ static bool AsmJSCacheOpenEntryForRead(JS::Handle<JSObject*> aGlobal, const char16_t* aBegin, const char16_t* aLimit, size_t* aSize, const uint8_t** aMemory, intptr_t *aHandle) { nsIPrincipal* principal = - nsJSPrincipals::get(JS_GetCompartmentPrincipals(js::GetObjectCompartment(aGlobal))); + nsJSPrincipals::get(JS::GetRealmPrincipals(js::GetNonCCWObjectRealm(aGlobal))); return asmjscache::OpenEntryForRead(principal, aBegin, aLimit, aSize, aMemory, aHandle); } static JS::AsmJSCacheResult AsmJSCacheOpenEntryForWrite(JS::Handle<JSObject*> aGlobal, const char16_t* aBegin, const char16_t* aEnd, size_t aSize, uint8_t** aMemory, intptr_t* aHandle) { nsIPrincipal* principal = - nsJSPrincipals::get(JS_GetCompartmentPrincipals(js::GetObjectCompartment(aGlobal))); + nsJSPrincipals::get(JS::GetRealmPrincipals(js::GetNonCCWObjectRealm(aGlobal))); return asmjscache::OpenEntryForWrite(principal, aBegin, aEnd, aSize, aMemory, aHandle); } class JSDispatchableRunnable final : public Runnable { ~JSDispatchableRunnable() {
--- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -2457,19 +2457,19 @@ GlobalObject::GetAsSupports() const nsIPrincipal* GlobalObject::GetSubjectPrincipal() const { if (!NS_IsMainThread()) { return nullptr; } - JSCompartment* compartment = js::GetContextCompartment(mCx); - MOZ_ASSERT(compartment); - JSPrincipals* principals = JS_GetCompartmentPrincipals(compartment); + JS::Realm* realm = js::GetContextRealm(mCx); + MOZ_ASSERT(realm); + JSPrincipals* principals = JS::GetRealmPrincipals(realm); return nsJSPrincipals::get(principals); } CallerType GlobalObject::CallerType() const { return nsContentUtils::ThreadsafeIsSystemCaller(mCx) ? dom::CallerType::System : dom::CallerType::NonSystem;
--- a/dom/bindings/CallbackObject.cpp +++ b/dom/bindings/CallbackObject.cpp @@ -128,20 +128,20 @@ CallbackObject::Callback(JSContext* aCx) MOZ_DIAGNOSTIC_ASSERT(callback); return callback; } CallbackObject::CallSetup::CallSetup(CallbackObject* aCallback, ErrorResult& aRv, const char* aExecutionReason, ExceptionHandling aExceptionHandling, - JSCompartment* aCompartment, + JS::Realm* aRealm, bool aIsJSImplementedWebIDL) : mCx(nullptr) - , mCompartment(aCompartment) + , mRealm(aRealm) , mErrorResult(aRv) , mExceptionHandling(aExceptionHandling) , mIsMainThread(NS_IsMainThread()) { CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get(); if (ccjs) { ccjs->EnterMicroTask(); } @@ -256,69 +256,69 @@ CallbackObject::CallSetup::CallSetup(Cal // And now we're ready to go. mCx = cx; } bool CallbackObject::CallSetup::ShouldRethrowException(JS::Handle<JS::Value> aException) { if (mExceptionHandling == eRethrowExceptions) { - if (!mCompartment) { + if (!mRealm) { // Caller didn't ask us to filter for only exceptions we subsume. return true; } // On workers, we don't have nsIPrincipals to work with. But we also only - // have one compartment, so check whether mCompartment is the same as the - // current compartment of mCx. - if (mCompartment == js::GetContextCompartment(mCx)) { + // have one realm, so check whether mRealm is the same as the current realm + // of mCx. + if (mRealm == js::GetContextRealm(mCx)) { return true; } MOZ_ASSERT(NS_IsMainThread()); - // At this point mCx is in the compartment of our unwrapped callback, so - // just check whether the principal of mCompartment subsumes that of the - // current compartment/global of mCx. + // At this point mCx is in the realm of our unwrapped callback, so just + // check whether the principal of mRealm subsumes that of the current + // realm/global of mCx. nsIPrincipal* callerPrincipal = - nsJSPrincipals::get(JS_GetCompartmentPrincipals(mCompartment)); + nsJSPrincipals::get(JS::GetRealmPrincipals(mRealm)); nsIPrincipal* calleePrincipal = nsContentUtils::SubjectPrincipal(); if (callerPrincipal->SubsumesConsideringDomain(calleePrincipal)) { return true; } } - MOZ_ASSERT(mCompartment); + MOZ_ASSERT(mRealm); // Now we only want to throw an exception to the caller if the object that was - // thrown is in the caller compartment (which we stored in mCompartment). + // thrown is in the caller realm (which we stored in mRealm). if (!aException.isObject()) { return false; } JS::Rooted<JSObject*> obj(mCx, &aException.toObject()); obj = js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false); - return js::GetObjectCompartment(obj) == mCompartment; + return js::GetNonCCWObjectRealm(obj) == mRealm; } CallbackObject::CallSetup::~CallSetup() { // To get our nesting right we have to destroy our JSAutoRealm first. // In particular, we want to do this before we try reporting any exceptions, // so we end up reporting them while in the realm of our entry point, // not whatever cross-compartment wrappper mCallback might be. // Be careful: the JSAutoRealm might not have been constructed at all! mAr.reset(); // Now, if we have a JSContext, report any pending errors on it, unless we // were told to re-throw them. if (mCx) { bool needToDealWithException = mAutoEntryScript->HasException(); - if ((mCompartment && mExceptionHandling == eRethrowContentExceptions) || + if ((mRealm && mExceptionHandling == eRethrowContentExceptions) || mExceptionHandling == eRethrowExceptions) { mErrorResult.MightThrowJSException(); if (needToDealWithException) { JS::Rooted<JS::Value> exn(mCx); if (mAutoEntryScript->PeekException(&exn) && ShouldRethrowException(exn)) { mAutoEntryScript->ClearException(); MOZ_ASSERT(!mAutoEntryScript->HasException()); @@ -344,17 +344,17 @@ CallbackObject::CallSetup::~CallSetup() } } } mAutoIncumbentScript.reset(); mAutoEntryScript.reset(); // It is important that this is the last thing we do, after leaving the - // compartment and undoing all our entry/incumbent script changes + // realm and undoing all our entry/incumbent script changes CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get(); if (ccjs) { ccjs->LeaveMicroTask(); } } already_AddRefed<nsISupports> CallbackObjectHolderBase::ToXPCOMCallback(CallbackObject* aCallback,
--- a/dom/bindings/CallbackObject.h +++ b/dom/bindings/CallbackObject.h @@ -89,17 +89,17 @@ public: // doesn't trigger any scripts before it accesses it. JS::Handle<JSObject*> CallbackOrNull() const { mCallback.exposeToActiveJS(); return CallbackPreserveColor(); } // Like CallbackOrNull(), but will return a new dead proxy object in the - // caller's compartment if the callback is null. + // caller's realm if the callback is null. JSObject* Callback(JSContext* aCx); JSObject* GetCreationStack() const { return mCreationStack; } void MarkForCC() @@ -141,20 +141,19 @@ public: enum ExceptionHandling { // Report any exception and don't throw it to the caller code. eReportExceptions, // Throw an exception to the caller code if the thrown exception is a // binding object for a DOMException from the caller's scope, otherwise // report it. eRethrowContentExceptions, - // Throw exceptions to the caller code, unless the caller compartment is + // Throw exceptions to the caller code, unless the caller realm is // provided, the exception is not a DOMException from the caller - // compartment, and the caller compartment does not subsume our unwrapped - // callback. + // realm, and the caller realm does not subsume our unwrapped callback. eRethrowExceptions }; size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { return aMallocSizeOf(this); } @@ -290,27 +289,27 @@ protected: { /** * A class that performs whatever setup we need to safely make a * call while this class is on the stack, After the constructor * returns, the call is safe to make if GetContext() returns * non-null. */ public: - // If aExceptionHandling == eRethrowContentExceptions then aCompartment - // needs to be set to the compartment in which exceptions will be rethrown. + // If aExceptionHandling == eRethrowContentExceptions then aRealm + // needs to be set to the realm in which exceptions will be rethrown. // - // If aExceptionHandling == eRethrowExceptions then aCompartment may be set - // to the compartment in which exceptions will be rethrown. In that case - // they will only be rethrown if that compartment's principal subsumes the + // If aExceptionHandling == eRethrowExceptions then aRealm may be set + // to the realm in which exceptions will be rethrown. In that case + // they will only be rethrown if that realm's principal subsumes the // principal of our (unwrapped) callback. CallSetup(CallbackObject* aCallback, ErrorResult& aRv, const char* aExecutionReason, ExceptionHandling aExceptionHandling, - JSCompartment* aCompartment = nullptr, + JS::Realm* aRealm = nullptr, bool aIsJSImplementedWebIDL = false); ~CallSetup(); JSContext* GetContext() const { return mCx; } @@ -318,19 +317,19 @@ protected: // We better not get copy-constructed CallSetup(const CallSetup&) = delete; bool ShouldRethrowException(JS::Handle<JS::Value> aException); // Members which can go away whenever JSContext* mCx; - // Caller's compartment. This will only have a sensible value if + // Caller's realm. This will only have a sensible value if // mExceptionHandling == eRethrowContentExceptions or eRethrowExceptions. - JSCompartment* mCompartment; + JS::Realm* mRealm; // And now members whose construction/destruction order we need to control. Maybe<AutoEntryScript> mAutoEntryScript; Maybe<AutoIncumbentScript> mAutoIncumbentScript; Maybe<JS::Rooted<JSObject*> > mRootedCallable; // Members which are used to set the async stack.
--- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -7231,19 +7231,19 @@ class CGCallGenerator(CGThing): principal = nullptr; } """) else: checkPrincipal = "" getPrincipal = fill( """ - JSCompartment* compartment = js::GetContextCompartment(cx); - MOZ_ASSERT(compartment); - JSPrincipals* principals = JS_GetCompartmentPrincipals(compartment); + JS::Realm* realm = js::GetContextRealm(cx); + MOZ_ASSERT(realm); + JSPrincipals* principals = JS::GetRealmPrincipals(realm); nsIPrincipal* principal = nsJSPrincipals::get(principals); ${checkPrincipal} """, checkPrincipal=checkPrincipal) if descriptor.interface.isExposedInAnyWorker(): self.cgRoot.prepend(CGGeneric(fill( """ @@ -7558,17 +7558,17 @@ class CGPerSignatureCall(CGThing): if descriptor.interface.isJSImplemented(): # We need the desired proto in our constructor, because the # constructor will actually construct our reflector. argsPost.append("desiredProto") elif descriptor.interface.isJSImplemented(): if not idlNode.isStatic(): needsUnwrap = True needsUnwrappedVar = True - argsPost.append("js::GetObjectCompartment(unwrappedObj ? *unwrappedObj : obj)") + argsPost.append("(unwrappedObj ? js::GetNonCCWObjectRealm(*unwrappedObj) : js::GetContextRealm(cx))") elif needScopeObject(returnType, arguments, self.extendedAttributes, descriptor.wrapperCache, True, idlNode.getExtendedAttribute("StoreInSlot")): needsUnwrap = True needsUnwrappedVar = True argsPre.append("unwrappedObj ? *unwrappedObj : obj") if needsUnwrap and needsUnwrappedVar: @@ -15004,17 +15004,17 @@ class CGJSImplMember(CGNativeMember): visibility=visibility, variadicIsSequence=variadicIsSequence, virtual=virtual, override=override) self.body = self.getImpl() def getArgs(self, returnType, argList): args = CGNativeMember.getArgs(self, returnType, argList) - args.append(Argument("JSCompartment*", "aCompartment", "nullptr")) + args.append(Argument("JS::Realm*", "aRealm", "nullptr")) return args class CGJSImplMethod(CGJSImplMember): """ Class for generating code for the methods for a JS-implemented WebIDL interface. """ @@ -15053,17 +15053,17 @@ class CGJSImplMethod(CGJSImplMember): # arguments to the WebIDL constructor, so don't pass them to # __Init(). The last argument is the prototype we're supposed to # use, and shouldn't get passed to __Init() either. assert args[0].argType == 'const GlobalObject&' assert args[1].argType == 'JSContext*' assert args[-1].argType == 'JS::Handle<JSObject*>' assert args[-1].name == 'aGivenProto' constructorArgs = [arg.name for arg in args[2:-1]] - constructorArgs.append("js::GetObjectCompartment(scopeObj)") + constructorArgs.append("js::GetNonCCWObjectRealm(scopeObj)") initCall = fill( """ // Wrap the object before calling __Init so that __DOM_IMPL__ is available. JS::Rooted<JSObject*> scopeObj(cx, globalHolder->GetGlobalJSObject()); MOZ_ASSERT(js::IsObjectInContextCompartment(scopeObj, cx)); JS::Rooted<JS::Value> wrappedVal(cx); if (!GetOrCreateDOMReflector(cx, impl, &wrappedVal, aGivenProto)) { MOZ_ASSERT(JS_IsExceptionPending(cx)); @@ -15501,61 +15501,61 @@ class CGCallback(CGClass): argnamesWithThis = ["s.GetContext()", "thisValJS"] + argnames argnamesWithoutThis = ["s.GetContext()", "JS::UndefinedHandleValue"] + argnames # Now that we've recorded the argnames for our call to our private # method, insert our optional argument for the execution reason. args.append(Argument("const char*", "aExecutionReason", "nullptr")) # Make copies of the arg list for the two "without rv" overloads. Note - # that those don't need aExceptionHandling or aCompartment arguments - # because those would make not sense anyway: the only sane thing to do - # with exceptions in the "without rv" cases is to report them. + # that those don't need aExceptionHandling or aRealm arguments because + # those would make not sense anyway: the only sane thing to do with + # exceptions in the "without rv" cases is to report them. argsWithoutRv = list(args) argsWithoutRv.pop(rvIndex) argsWithoutThisAndRv = list(argsWithoutRv) # Add the potional argument for deciding whether the CallSetup should # re-throw exceptions on aRv. args.append(Argument("ExceptionHandling", "aExceptionHandling", "eReportExceptions")) # And the argument for communicating when exceptions should really be # rethrown. In particular, even when aExceptionHandling is - # eRethrowExceptions they won't get rethrown if aCompartment is provided + # eRethrowExceptions they won't get rethrown if aRealm is provided # and its principal doesn't subsume either the callback or the # exception. - args.append(Argument("JSCompartment*", "aCompartment", "nullptr")) + args.append(Argument("JS::Realm*", "aRealm", "nullptr")) # And now insert our template argument. argsWithoutThis = list(args) args.insert(0, Argument("const T&", "thisVal")) argsWithoutRv.insert(0, Argument("const T&", "thisVal")) argnamesWithoutThisAndRv = [arg.name for arg in argsWithoutThisAndRv] argnamesWithoutThisAndRv.insert(rvIndex, "IgnoreErrors()"); # If we just leave things like that, and have no actual arguments in the # IDL, we will end up trying to call the templated "without rv" overload # with "rv" as the thisVal. That's no good. So explicitly append the - # aExceptionHandling and aCompartment values we need to end up matching - # the signature of our non-templated "with rv" overload. + # aExceptionHandling and aRealm values we need to end up matching the + # signature of our non-templated "with rv" overload. argnamesWithoutThisAndRv.extend(["eReportExceptions", "nullptr"]) argnamesWithoutRv = [arg.name for arg in argsWithoutRv] # Note that we need to insert at rvIndex + 1, since we inserted a # thisVal arg at the start. argnamesWithoutRv.insert(rvIndex + 1, "IgnoreErrors()") errorReturn = method.getDefaultRetval() setupCall = fill( """ MOZ_ASSERT(!aRv.Failed(), "Don't pass an already-failed ErrorResult to a callback!"); if (!aExecutionReason) { aExecutionReason = "${executionReason}"; } - CallSetup s(this, aRv, aExecutionReason, aExceptionHandling, aCompartment); + CallSetup s(this, aRv, aExecutionReason, aExceptionHandling, aRealm); if (!s.GetContext()) { MOZ_ASSERT(aRv.Failed()); return${errorReturn}; } """, errorReturn=errorReturn, executionReason=method.getPrettyName()) @@ -15956,35 +15956,35 @@ class CallbackMember(CGNativeMember): if not self.needThisHandling: # Since we don't need this handling, we're the actual method that # will be called, so we need an aRethrowExceptions argument. if not self.rethrowContentException: args.append(Argument("const char*", "aExecutionReason", "nullptr")) args.append(Argument("ExceptionHandling", "aExceptionHandling", "eReportExceptions")) - args.append(Argument("JSCompartment*", "aCompartment", "nullptr")) + args.append(Argument("JS::Realm*", "aRealm", "nullptr")) return args # We want to allow the caller to pass in a "this" value, as # well as a JSContext. return [Argument("JSContext*", "cx"), Argument("JS::Handle<JS::Value>", "aThisVal")] + args def getCallSetup(self): if self.needThisHandling: # It's been done for us already return "" callSetup = "CallSetup s(this, aRv" if self.rethrowContentException: # getArgs doesn't add the aExceptionHandling argument but does add - # aCompartment for us. - callSetup += ', "%s", eRethrowContentExceptions, aCompartment, /* aIsJSImplementedWebIDL = */ ' % self.getPrettyName() + # aRealm for us. + callSetup += ', "%s", eRethrowContentExceptions, aRealm, /* aIsJSImplementedWebIDL = */ ' % self.getPrettyName() callSetup += toStringBool(isJSImplementedDescriptor(self.descriptorProvider)) else: - callSetup += ', "%s", aExceptionHandling, aCompartment' % self.getPrettyName() + callSetup += ', "%s", aExceptionHandling, aRealm' % self.getPrettyName() callSetup += ");\n" return fill( """ $*{callSetup} JSContext* cx = s.GetContext(); if (!cx) { MOZ_ASSERT(aRv.Failed()); return${errorReturn};
--- a/dom/bindings/Exceptions.cpp +++ b/dom/bindings/Exceptions.cpp @@ -184,17 +184,17 @@ CreateException(nsresult aRv, const nsAC } already_AddRefed<nsIStackFrame> GetCurrentJSStack(int32_t aMaxDepth) { // is there a current context available? JSContext* cx = nsContentUtils::GetCurrentJSContext(); - if (!cx || !js::GetContextCompartment(cx)) { + if (!cx || !js::GetContextRealm(cx)) { return nullptr; } static const unsigned MAX_FRAMES = 100; if (aMaxDepth < 0) { aMaxDepth = MAX_FRAMES; }
--- a/dom/bindings/test/mochitest.ini +++ b/dom/bindings/test/mochitest.ini @@ -68,16 +68,18 @@ skip-if = debug == false [test_unforgeablesonexpando.html] [test_crossOriginWindowSymbolAccess.html] [test_primitive_this.html] [test_callback_exceptions.html] [test_bug1123516_maplikesetlike.html] skip-if = debug == false [test_jsimplemented_eventhandler.html] skip-if = debug == false +[test_jsimplemented_cross_realm_this.html] +skip-if = debug == false [test_iterable.html] skip-if = debug == false [test_oom_reporting.html] [test_domProxyArrayLengthGetter.html] [test_exceptionSanitization.html] skip-if = debug == false [test_stringBindings.html] skip-if = debug == false
new file mode 100644 --- /dev/null +++ b/dom/bindings/test/test_jsimplemented_cross_realm_this.html @@ -0,0 +1,43 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1464374--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1464374</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1464374">Mozilla Bug 1464374</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> + +<iframe></iframe> +<script type="application/javascript"> + /** Test for Bug 1464374 **/ + SimpleTest.waitForExplicitFinish(); + + function doTest() { + var frame = frames[0]; + var obj = new frame.TestInterfaceJS(); + var ex; + try { + TestInterfaceJS.prototype.testThrowTypeError.call(obj); + } catch(e) { + ex = e; + } + ok(ex, "Should have an exception"); + SimpleTest.finish(); + } + + SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]}, + doTest); +</script> + +</body> +</html>
--- a/dom/html/nsHTMLDocument.cpp +++ b/dom/html/nsHTMLDocument.cpp @@ -1532,20 +1532,20 @@ nsHTMLDocument::Open(JSContext* cx, CreateAndAddWyciwygChannel(); --mWriteLevel; SetReadyStateInternal(nsIDocument::READYSTATE_LOADING); // After changing everything around, make sure that the principal on the - // document's compartment exactly matches NodePrincipal(). + // document's realm exactly matches NodePrincipal(). DebugOnly<JSObject*> wrapper = GetWrapperPreserveColor(); MOZ_ASSERT_IF(wrapper, - JS_GetCompartmentPrincipals(js::GetObjectCompartment(wrapper)) == + JS::GetRealmPrincipals(js::GetNonCCWObjectRealm(wrapper)) == nsJSPrincipals::get(NodePrincipal())); return kungFuDeathGrip.forget(); } void nsHTMLDocument::Close(ErrorResult& rv) {
--- a/dom/script/ScriptSettings.cpp +++ b/dom/script/ScriptSettings.cpp @@ -613,17 +613,17 @@ AutoJSAPI::ReportException() } } bool AutoJSAPI::PeekException(JS::MutableHandle<JS::Value> aVal) { MOZ_ASSERT_IF(mIsMainThread, IsStackTop()); MOZ_ASSERT(HasException()); - MOZ_ASSERT(js::GetContextCompartment(cx())); + MOZ_ASSERT(js::GetContextRealm(cx())); if (!JS_GetPendingException(cx(), aVal)) { return false; } return true; } bool AutoJSAPI::StealException(JS::MutableHandle<JS::Value> aVal)
--- a/dom/security/nsContentSecurityManager.cpp +++ b/dom/security/nsContentSecurityManager.cpp @@ -885,22 +885,26 @@ nsContentSecurityManager::IsOriginPotent NS_WARNING_ASSERTION(!scheme.EqualsLiteral("blob"), "IsOriginPotentiallyTrustworthy ignoring blob scheme"); // According to the specification, the user agent may choose to extend the // trust to other, vendor-specific URL schemes. We use this for "resource:", // which is technically a substituting protocol handler that is not limited to // local resource mapping, but in practice is never mapped remotely as this // would violate assumptions a lot of code makes. - if (scheme.EqualsLiteral("https") || - scheme.EqualsLiteral("file") || - scheme.EqualsLiteral("resource") || - scheme.EqualsLiteral("app") || - scheme.EqualsLiteral("moz-extension") || - scheme.EqualsLiteral("wss")) { + // We use nsIProtocolHandler flags to determine which protocols we consider a priori + // authenticated. + bool aPrioriAuthenticated = false; + if (NS_FAILED(NS_URIChainHasFlags(uri, + nsIProtocolHandler::URI_IS_POTENTIALLY_TRUSTWORTHY, + &aPrioriAuthenticated))) { + return NS_ERROR_UNEXPECTED; + } + + if (aPrioriAuthenticated) { *aIsTrustWorthy = true; return NS_OK; } nsAutoCString host; rv = uri->GetHost(host); if (NS_FAILED(rv)) { return NS_OK;
--- a/dom/security/nsMixedContentBlocker.cpp +++ b/dom/security/nsMixedContentBlocker.cpp @@ -592,29 +592,29 @@ nsMixedContentBlocker::ShouldLoad(bool a * URI_DOES_NOT_RETURN_DATA - e.g. * "mailto" * URI_IS_LOCAL_RESOURCE - e.g. * "data", * "resource", * "moz-icon" * URI_INHERITS_SECURITY_CONTEXT - e.g. * "javascript" - * URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT - e.g. + * URI_IS_POTENTIALLY_TRUSTWORTHY - e.g. * "https", * "moz-safe-about" * */ bool schemeLocal = false; bool schemeNoReturnData = false; bool schemeInherits = false; bool schemeSecure = false; if (NS_FAILED(NS_URIChainHasFlags(innerContentLocation, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE , &schemeLocal)) || NS_FAILED(NS_URIChainHasFlags(innerContentLocation, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA, &schemeNoReturnData)) || NS_FAILED(NS_URIChainHasFlags(innerContentLocation, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, &schemeInherits)) || - NS_FAILED(NS_URIChainHasFlags(innerContentLocation, nsIProtocolHandler::URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT, &schemeSecure))) { + NS_FAILED(NS_URIChainHasFlags(innerContentLocation, nsIProtocolHandler::URI_IS_POTENTIALLY_TRUSTWORTHY, &schemeSecure))) { *aDecision = REJECT_REQUEST; return NS_ERROR_FAILURE; } // TYPE_IMAGE redirects are cached based on the original URI, not the final // destination and hence cache hits for images may not have the correct // innerContentLocation. Check if the cached hit went through an http redirect, // and if it did, we can't treat this as a secure subresource. if (!aHadInsecureImageRedirect &&
new file mode 100644 --- /dev/null +++ b/dom/security/test/gtest/TestSecureContext.cpp @@ -0,0 +1,94 @@ +/* -*- 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 "gtest/gtest.h" + +#include <string.h> +#include <stdlib.h> + +#include "nsContentSecurityManager.h" +#include "nsContentUtils.h" +#include "nsIPrincipal.h" +#include "nsScriptSecurityManager.h" +#include "NullPrincipal.h" + +static const uint32_t kURIMaxLength = 64; + +struct TestExpectations { + char uri[kURIMaxLength ]; + bool expectedResult; +}; + +// ============================= TestDirectives ======================== + +TEST(SecureContext, IsOriginPotentiallyTrustworthyWithCodeBasePrincipal) +{ + //boolean isOriginPotentiallyTrustworthy(in nsIPrincipal aPrincipal); + + static const TestExpectations uris[] = { + { "http://example.com/", false }, + { "https://example.com/", true }, + { "ws://example.com/", false }, + { "wss://example.com/", true }, + { "file:///xyzzy", true }, + { "ftp://example.com", false }, + { "about:config", false }, + { "http://localhost", true }, + { "http://xyzzy.localhost", false }, + { "http://127.0.0.1", true }, + { "resource://xyzzy", true }, + { "moz-extension://xyzzy", true }, + { "data:data:text/plain;charset=utf-8;base64,eHl6enk=", false }, + { "blob://unique-id", false }, + { "mailto:foo@bar.com", false }, + { "moz-icon://example.com", false }, + { "javascript:42", false }, + }; + + uint32_t numExpectations = sizeof(uris) / sizeof(TestExpectations); + nsCOMPtr<nsIContentSecurityManager> csManager = do_GetService(NS_CONTENTSECURITYMANAGER_CONTRACTID); + ASSERT_TRUE(!!csManager); + + nsresult rv; + for (uint32_t i = 0; i < numExpectations; i++) { + nsCOMPtr<nsIPrincipal> prin; + nsAutoCString uri(uris[i].uri); + rv = nsScriptSecurityManager::GetScriptSecurityManager()-> + CreateCodebasePrincipalFromOrigin(uri, getter_AddRefs(prin)); + bool isPotentiallyTrustworthy = false; + rv = csManager->IsOriginPotentiallyTrustworthy(prin, &isPotentiallyTrustworthy); + ASSERT_EQ(NS_OK, rv); + ASSERT_EQ(isPotentiallyTrustworthy, uris[i].expectedResult); + } +} + +TEST(SecureContext, IsOriginPotentiallyTrustworthyWithSystemPrincipal) +{ + RefPtr<nsScriptSecurityManager> ssManager = nsScriptSecurityManager::GetScriptSecurityManager(); + ASSERT_TRUE(!!ssManager); + nsCOMPtr<nsIContentSecurityManager> csManager = do_GetService(NS_CONTENTSECURITYMANAGER_CONTRACTID); + ASSERT_TRUE(!!csManager); + + nsCOMPtr<nsIPrincipal> sysPrin = nsContentUtils::GetSystemPrincipal(); + bool isPotentiallyTrustworthy; + nsresult rv = csManager->IsOriginPotentiallyTrustworthy(sysPrin, &isPotentiallyTrustworthy); + ASSERT_EQ(rv, NS_OK); + ASSERT_TRUE(isPotentiallyTrustworthy); +} + +TEST(SecureContext, IsOriginPotentiallyTrustworthyWithNullPrincipal) +{ + RefPtr<nsScriptSecurityManager> ssManager = nsScriptSecurityManager::GetScriptSecurityManager(); + ASSERT_TRUE(!!ssManager); + nsCOMPtr<nsIContentSecurityManager> csManager = do_GetService(NS_CONTENTSECURITYMANAGER_CONTRACTID); + ASSERT_TRUE(!!csManager); + + RefPtr<NullPrincipal> nullPrin = NullPrincipal::CreateWithoutOriginAttributes(); + bool isPotentiallyTrustworthy; + nsresult rv = csManager->IsOriginPotentiallyTrustworthy(nullPrin, &isPotentiallyTrustworthy); + ASSERT_EQ(rv, NS_OK); + ASSERT_TRUE(!isPotentiallyTrustworthy); +}
--- a/dom/security/test/gtest/moz.build +++ b/dom/security/test/gtest/moz.build @@ -1,11 +1,16 @@ # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. UNIFIED_SOURCES += [ 'TestCSPParser.cpp', + 'TestSecureContext.cpp', ] FINAL_LIBRARY = 'xul-gtest' + +LOCAL_INCLUDES += [ + '/caps', +]
--- a/dom/tests/mochitest/chrome/chrome.ini +++ b/dom/tests/mochitest/chrome/chrome.ini @@ -41,22 +41,20 @@ support-files = [test_DOM_element_instanceof.xul] [test_activation.xul] tags = fullscreen [test_bug799299.xul] [test_bug800817.xul] [test_bug830858.xul] [test_bug1224790-1.xul] tags = openwindow -# synthesizeNativeOSXClick does not work on 10.6 -skip-if = os != 'mac' || os_version == '10.6' +skip-if = os != 'mac' [test_bug1224790-2.xul] tags = openwindow -# synthesizeNativeOSXClick does not work on 10.6 -skip-if = os != 'mac' || os_version == '10.6' +skip-if = os != 'mac' [test_callback_wrapping.xul] [test_clonewrapper.xul] [test_cyclecollector.xul] [test_docshell_swap.xul] [test_focus.xul] skip-if = os == 'linux' && !debug # bug 1296622 [test_focus_docnav.xul] [test_focus_switchbinding.xul]
--- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -1201,25 +1201,22 @@ public: override { MOZ_ASSERT(!aRealmStats->extra); // ReportJSRuntimeExplicitTreeStats expects that // aRealmStats->extra is a xpc::RealmStatsExtras pointer. xpc::RealmStatsExtras* extras = new xpc::RealmStatsExtras; - // This is the |jsPathPrefix|. Each worker has exactly two realms: - // one for atoms, and one for everything else. + // This is the |jsPathPrefix|. Each worker has exactly one realm. JSCompartment* compartment = JS::GetCompartmentForRealm(aRealm); extras->jsPathPrefix.Assign(mRtPath); extras->jsPathPrefix += nsPrintfCString("zone(0x%p)/", (void *)js::GetCompartmentZone(compartment)); - extras->jsPathPrefix += js::IsAtomsRealm(aRealm) - ? NS_LITERAL_CSTRING("realm(web-worker-atoms)/") - : NS_LITERAL_CSTRING("realm(web-worker)/"); + extras->jsPathPrefix += NS_LITERAL_CSTRING("realm(web-worker)/"); // This should never be used when reporting with workers (hence the "?!"). extras->domPathPrefix.AssignLiteral("explicit/workers/?!/"); MOZ_ASSERT(StartsWithExplicit(extras->jsPathPrefix)); MOZ_ASSERT(StartsWithExplicit(extras->domPathPrefix)); extras->location = nullptr;
--- a/gfx/config/gfxVars.h +++ b/gfx/config/gfxVars.h @@ -34,19 +34,21 @@ class gfxVarReceiver; _(PDMWMFDisableD3D11Dlls, nsCString, nsCString()) \ _(PDMWMFDisableD3D9Dlls, nsCString, nsCString()) \ _(DXInterop2Blocked, bool, false) \ _(DXNV12Blocked, bool, false) \ _(UseWebRender, bool, false) \ _(UseWebRenderANGLE, bool, false) \ _(UseWebRenderDCompWin, bool, false) \ _(UseWebRenderProgramBinary, bool, false) \ + _(UseWebRenderProgramBinaryDisk, bool, false) \ _(WebRenderDebugFlags, int32_t, 0) \ _(ScreenDepth, int32_t, 0) \ _(GREDirectory, nsString, nsString()) \ + _(ProfDirectory, nsString, nsString()) \ _(UseOMTP, bool, false) \ _(AllowD3D11KeyedMutex, bool, false) \ /* Add new entries above this line. */ // Some graphics settings are computed on the UI process and must be // communicated to content and GPU processes. gfxVars helps facilitate // this. Its function is similar to gfxPrefs, except rather than hold
--- a/gfx/layers/client/MultiTiledContentClient.cpp +++ b/gfx/layers/client/MultiTiledContentClient.cpp @@ -321,17 +321,16 @@ void ClientMultiTiledLayerBuffer::Update // Reset mPaintTiles.clear(); mTilingOrigin = IntPoint(std::numeric_limits<int32_t>::max(), std::numeric_limits<int32_t>::max()); } bool edgePaddingEnabled = gfxPrefs::TileEdgePaddingEnabled(); - for (uint32_t i = 0; i < mRetainedTiles.Length(); ++i) { TileClient& tile = mRetainedTiles[i]; // Only worry about padding when not doing low-res because it simplifies // the math and the artifacts won't be noticable // Edge padding prevents sampling artifacts when compositing. if (edgePaddingEnabled && mResolution == 1 && tile.mFrontBuffer && tile.mFrontBuffer->IsLocked()) { @@ -437,16 +436,18 @@ ClientMultiTiledLayerBuffer::ValidateTil // Add the region we copied from the front buffer into the painted region extraPainted.MoveBy(aTileOrigin); extraPainted.And(extraPainted, mNewValidRegion); if (!backBuffer) { return false; } + // Get the targets to draw into, and create a dual target + // if we are using component alpha RefPtr<DrawTarget> dt = backBuffer->BorrowDrawTarget(); RefPtr<DrawTarget> dtOnWhite; if (backBufferOnWhite) { dtOnWhite = backBufferOnWhite->BorrowDrawTarget(); } if (!dt || (backBufferOnWhite && !dtOnWhite)) { aTile.DiscardBuffers(); @@ -455,49 +456,59 @@ ClientMultiTiledLayerBuffer::ValidateTil RefPtr<DrawTarget> drawTarget; if (dtOnWhite) { drawTarget = Factory::CreateDualDrawTarget(dt, dtOnWhite); } else { drawTarget = dt; } - auto clear = CapturedTiledPaintState::Clear{ - dt, - dtOnWhite, - tileDirtyRegion - }; + // We need to clear the dirty region of the tile before painting + // if we are painting non-opaque content + Maybe<CapturedTiledPaintState::Clear> clear = Nothing(); + if (mode != SurfaceMode::SURFACE_OPAQUE) { + clear = Some(CapturedTiledPaintState::Clear{ + dt, + dtOnWhite, + tileDirtyRegion + }); + } + // Queue or execute the paint operation gfx::Tile paintTile; paintTile.mTileOrigin = gfx::IntPoint(aTileOrigin.x, aTileOrigin.y); if (aFlags & TilePaintFlags::Async) { RefPtr<CapturedTiledPaintState> asyncPaint = new CapturedTiledPaintState(); RefPtr<DrawTargetCapture> captureDT = Factory::CreateCaptureDrawTarget(drawTarget->GetBackendType(), drawTarget->GetSize(), drawTarget->GetFormat()); paintTile.mDrawTarget = captureDT; asyncPaint->mTarget = drawTarget; asyncPaint->mCapture = captureDT; asyncPaint->mCopies = std::move(asyncPaintCopies); - asyncPaint->mClears.push_back(clear); + if (clear) { + asyncPaint->mClears.push_back(*clear); + } asyncPaint->mClients = std::move(asyncPaintClients); asyncPaint->mClients.push_back(backBuffer); if (backBufferOnWhite) { asyncPaint->mClients.push_back(backBufferOnWhite); } mPaintStates.push_back(asyncPaint); } else { paintTile.mDrawTarget = drawTarget; - clear.ClearBuffer(); + if (clear) { + clear->ClearBuffer(); + } } mPaintTiles.push_back(paintTile); mTilingOrigin.x = std::min(mTilingOrigin.x, paintTile.mTileOrigin.x); mTilingOrigin.y = std::min(mTilingOrigin.y, paintTile.mTileOrigin.y); // The new buffer is now validated, remove the dirty region from it.
--- a/gfx/layers/client/SingleTiledContentClient.cpp +++ b/gfx/layers/client/SingleTiledContentClient.cpp @@ -192,78 +192,31 @@ ClientSingleTiledLayerBuffer::PaintThebe // If the old frontbuffer was discarded then attempt to copy what we // can from it to the new backbuffer. if (discardedFrontBuffer) { nsIntRegion copyableRegion; copyableRegion.And(aNewValidRegion, discardedValidRegion); copyableRegion.SubOut(aDirtyRegion); - if (!copyableRegion.IsEmpty()) { - OpenMode asyncFlags = asyncPaint ? OpenMode::OPEN_ASYNC - : OpenMode::OPEN_NONE; - - TextureClientAutoLock frontLock(discardedFrontBuffer, - OpenMode::OPEN_READ | asyncFlags); - Maybe<TextureClientAutoLock> frontOnWhiteLock; - if (discardedFrontBufferOnWhite && backBufferOnWhite) { - frontOnWhiteLock.emplace(discardedFrontBufferOnWhite, OpenMode::OPEN_READ | asyncFlags); - } - - // Copy to both backBuffer and backBufferOnWhite if required, or copy to neither. - if (frontLock.Succeeded() && (!frontOnWhiteLock || frontOnWhiteLock->Succeeded())) { - RefPtr<gfx::DrawTarget> frontBuffer = discardedFrontBuffer->BorrowDrawTarget(); - - if (frontBuffer) { - for (auto iter = copyableRegion.RectIter(); !iter.Done(); iter.Next()) { - const gfx::IntRect rect = iter.Get() - discardedValidRegion.GetBounds().TopLeft(); - const gfx::IntPoint dest = iter.Get().TopLeft() - mTilingOrigin; - - auto copy = CapturedTiledPaintState::Copy{ - frontBuffer, dt, rect, dest - }; - if (asyncPaint) { - paintCopies.push_back(copy); - } else { - copy.CopyBuffer(); - } - } + if (!mTile.CopyFromBuffer(discardedFrontBuffer, + discardedFrontBufferOnWhite, + discardedValidRegion.GetBounds().TopLeft(), + mTilingOrigin, + copyableRegion, + aFlags, + &paintCopies)) { + gfxWarning() << "[Tiling:Client] Failed to aquire the discarded front buffer's draw target"; + } else { + TILING_LOG("TILING %p: Region copied from discarded frontbuffer %s\n", &mPaintedLayer, Stringify(copyableRegion).c_str()); - if (frontOnWhiteLock) { - RefPtr<gfx::DrawTarget> frontBufferOnWhite = discardedFrontBufferOnWhite->BorrowDrawTarget(); - - if (frontBufferOnWhite) { - for (auto iter = copyableRegion.RectIter(); !iter.Done(); iter.Next()) { - const gfx::IntRect rect = iter.Get() - discardedValidRegion.GetBounds().TopLeft(); - const gfx::IntPoint dest = iter.Get().TopLeft() - mTilingOrigin; - - auto copy = CapturedTiledPaintState::Copy{ - frontBufferOnWhite, dtOnWhite, rect, dest - }; - if (asyncPaint) { - paintCopies.push_back(copy); - } else { - copy.CopyBuffer(); - } - } - } else { - gfxWarning() << "[Tiling:Client] Failed to aquire the discarded front buffer's draw target"; - } - } - } else { - gfxWarning() << "[Tiling:Client] Failed to aquire the discarded front buffer's draw target"; - } - - TILING_LOG("TILING %p: Region copied from discarded frontbuffer %s\n", &mPaintedLayer, Stringify(copyableRegion).c_str()); - - // We don't need to repaint valid content that was just copied. - paintRegion.SubOut(copyableRegion); - copyableRegion.MoveBy(-mTilingOrigin); - tileDirtyRegion.SubOut(copyableRegion); - } + // We don't need to repaint valid content that was just copied. + paintRegion.SubOut(copyableRegion); + copyableRegion.MoveBy(-mTilingOrigin); + tileDirtyRegion.SubOut(copyableRegion); } } if (mode != SurfaceMode::SURFACE_OPAQUE) { auto clear = CapturedTiledPaintState::Clear{ dt, dtOnWhite, tileDirtyRegion,
--- a/gfx/layers/client/TiledContentClient.cpp +++ b/gfx/layers/client/TiledContentClient.cpp @@ -478,55 +478,59 @@ CopyFrontToBack(TextureClient* aFront, void TileClient::ValidateBackBufferFromFront(const nsIntRegion& aDirtyRegion, const nsIntRegion& aVisibleRegion, nsIntRegion& aAddPaintedRegion, TilePaintFlags aFlags, std::vector<CapturedTiledPaintState::Copy>* aCopies, std::vector<RefPtr<TextureClient>>* aClients) { - if (mBackBuffer && mFrontBuffer) { - gfx::IntSize tileSize = mFrontBuffer->GetSize(); - const IntRect tileRect = IntRect(0, 0, tileSize.width, tileSize.height); + if (!mBackBuffer || !mFrontBuffer) { + return; + } + + gfx::IntSize tileSize = mFrontBuffer->GetSize(); + const IntRect tileRect = IntRect(0, 0, tileSize.width, tileSize.height); - if (aDirtyRegion.Contains(tileRect)) { - // The dirty region means that we no longer need the front buffer, so - // discard it. - DiscardFrontBuffer(); - } else { - // Region that needs copying. - nsIntRegion regionToCopy = mInvalidBack; + if (aDirtyRegion.Contains(tileRect)) { + // The dirty region means that we no longer need the front buffer, so + // discard it. + DiscardFrontBuffer(); + return; + } - regionToCopy.Sub(regionToCopy, aDirtyRegion); - regionToCopy.And(regionToCopy, aVisibleRegion); + // Region that needs copying. + nsIntRegion regionToCopy = mInvalidBack; - aAddPaintedRegion = regionToCopy; + regionToCopy.Sub(regionToCopy, aDirtyRegion); + regionToCopy.And(regionToCopy, aVisibleRegion); - if (regionToCopy.IsEmpty()) { - // Just redraw it all. - return; - } + aAddPaintedRegion = regionToCopy; + + if (regionToCopy.IsEmpty()) { + // Just redraw it all. + return; + } - // Copy the bounding rect of regionToCopy. As tiles are quite small, it - // is unlikely that we'd save much by copying each individual rect of the - // region, but we can reevaluate this if it becomes an issue. - const IntRect rectToCopy = regionToCopy.GetBounds(); - gfx::IntRect gfxRectToCopy(rectToCopy.X(), rectToCopy.Y(), rectToCopy.Width(), rectToCopy.Height()); - if (CopyFrontToBack(mFrontBuffer, mBackBuffer, gfxRectToCopy, aFlags, aCopies, aClients)) { - if (mBackBufferOnWhite) { - MOZ_ASSERT(mFrontBufferOnWhite); - if (CopyFrontToBack(mFrontBufferOnWhite, mBackBufferOnWhite, gfxRectToCopy, aFlags, aCopies, aClients)) { - mInvalidBack.Sub(mInvalidBack, aVisibleRegion); - } - } else { - mInvalidBack.Sub(mInvalidBack, aVisibleRegion); - } - } + // Copy the bounding rect of regionToCopy. As tiles are quite small, it + // is unlikely that we'd save much by copying each individual rect of the + // region, but we can reevaluate this if it becomes an issue. + const IntRect rectToCopy = regionToCopy.GetBounds(); + if (!CopyFrontToBack(mFrontBuffer, mBackBuffer, rectToCopy, aFlags, aCopies, aClients)) { + return; + } + + if (mBackBufferOnWhite) { + MOZ_ASSERT(mFrontBufferOnWhite); + if (!CopyFrontToBack(mFrontBufferOnWhite, mBackBufferOnWhite, rectToCopy, aFlags, aCopies, aClients)) { + return; } } + + mInvalidBack.Sub(mInvalidBack, aVisibleRegion); } void TileClient::DiscardFrontBuffer() { if (mFrontBuffer) { MOZ_ASSERT(mFrontBuffer->GetReadLock()); @@ -575,16 +579,69 @@ TileClient::DiscardBackBuffer() if (mBackBuffer) { DiscardTexture(mBackBuffer, mAllocator); mBackBuffer.Set(this, nullptr); DiscardTexture(mBackBufferOnWhite, mAllocator); mBackBufferOnWhite = nullptr; } } +bool +TileClient::CopyFromBuffer(RefPtr<TextureClient> aBuffer, + RefPtr<TextureClient> aBufferOnWhite, + nsIntPoint aBufferOrigin, + nsIntPoint aTileOrigin, + const nsIntRegion& aRegion, + TilePaintFlags aFlags, + std::vector<CapturedTiledPaintState::Copy>* aCopies) +{ + if (aRegion.IsEmpty()) { + return true; + } + + bool asyncPaint = !!(aFlags & TilePaintFlags::Async); + auto CopyBuffer = [&aRegion, asyncPaint, &aCopies] (auto aSrc, auto aSrcOrigin, auto aDest, auto aDestOrigin) { + MOZ_ASSERT(aDest->IsLocked()); + + OpenMode asyncFlags = asyncPaint ? OpenMode::OPEN_ASYNC + : OpenMode::OPEN_NONE; + TextureClientAutoLock lock(aSrc, OpenMode::OPEN_READ | asyncFlags); + if (!lock.Succeeded()) { + return false; + } + + RefPtr<gfx::DrawTarget> srcTarget = aSrc->BorrowDrawTarget(); + RefPtr<gfx::DrawTarget> destTarget = aDest->BorrowDrawTarget(); + if (!srcTarget || !destTarget) { + return false; + } + + for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) { + const gfx::IntRect src = iter.Get() - aSrcOrigin; + const gfx::IntPoint dest = iter.Get().TopLeft() - aDestOrigin; + + auto copy = CapturedTiledPaintState::Copy{ + srcTarget, destTarget, src, dest + }; + + if (asyncPaint) { + aCopies->push_back(copy); + } else { + copy.CopyBuffer(); + } + } + return true; + }; + + return CopyBuffer(aBuffer, aBufferOrigin, mBackBuffer, aTileOrigin) && + (!aBufferOnWhite || + !mBackBufferOnWhite || + CopyBuffer(aBufferOnWhite, aBufferOrigin, mBackBufferOnWhite, aTileOrigin)); +} + static already_AddRefed<TextureClient> CreateBackBufferTexture(TextureClient* aCurrentTexture, CompositableClient& aCompositable, TextureClientAllocator* aAllocator) { if (aCurrentTexture) { // Our current back-buffer is still locked by the compositor. This can occur // when the client is producing faster than the compositor can consume. In
--- a/gfx/layers/client/TiledContentClient.h +++ b/gfx/layers/client/TiledContentClient.h @@ -135,16 +135,28 @@ struct TileClient RefPtr<TextureClient>* aTextureClientOnWhite, std::vector<CapturedTiledPaintState::Copy>* aCopies, std::vector<RefPtr<TextureClient>>* aClients); void DiscardFrontBuffer(); void DiscardBackBuffer(); + /* + * Copy aRegion from aBuffer and aBufferOnWhite positioned at aBufferOrigin + * into ourselves assuming we are positioned at aTileOrigin. + */ + bool CopyFromBuffer(RefPtr<TextureClient> aBuffer, + RefPtr<TextureClient> aBufferOnWhite, + nsIntPoint aBufferOrigin, + nsIntPoint aTileOrigin, + const nsIntRegion& aRegion, + TilePaintFlags aFlags, + std::vector<CapturedTiledPaintState::Copy>* aCopies); + /* We wrap the back buffer in a class that disallows assignment * so that we can track when ever it changes so that we can update * the expiry tracker for expiring the back buffers */ class PrivateProtector { public: void Set(TileClient * container, RefPtr<TextureClient>); void Set(TileClient * container, TextureClient*); // Implicitly convert to TextureClient* because we can't chain @@ -163,18 +175,20 @@ struct TileClient bool mWasPlaceholder; #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY TimeStamp mLastUpdate; #endif nsIntRegion mInvalidFront; nsIntRegion mInvalidBack; nsExpirationState mExpirationState; private: - // Copies dirty pixels from the front buffer into the back buffer, - // and records the copied region in aAddPaintedRegion. + /* + * Copies dirty pixels from the front buffer into the back buffer, + * and records the copied region in aAddPaintedRegion. + */ void ValidateBackBufferFromFront(const nsIntRegion &aDirtyRegion, const nsIntRegion& aVisibleRegion, nsIntRegion& aAddPaintedRegion, TilePaintFlags aFlags, std::vector<CapturedTiledPaintState::Copy>* aCopies, std::vector<RefPtr<TextureClient>>* aClients); };
--- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -17,16 +17,17 @@ #include "mozilla/gfx/GraphicsMessages.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/Telemetry.h" #include "mozilla/TimeStamp.h" #include "mozilla/Unused.h" #include "mozilla/Logging.h" #include "mozilla/Services.h" +#include "nsAppDirectoryServiceDefs.h" #include "gfxCrashReporterUtils.h" #include "gfxPlatform.h" #include "gfxPrefs.h" #include "gfxEnv.h" #include "gfxTextRun.h" #include "gfxUserFontSet.h" #include "gfxConfig.h" @@ -861,16 +862,28 @@ gfxPlatform::Init() Preferences::Unlock(FONT_VARIATIONS_PREF); if (!gPlatform->HasVariationFontSupport()) { // Ensure variation fonts are disabled and the pref is locked. Preferences::SetBool(FONT_VARIATIONS_PREF, false, PrefValueKind::Default); Preferences::SetBool(FONT_VARIATIONS_PREF, false); Preferences::Lock(FONT_VARIATIONS_PREF); } + + nsCOMPtr<nsIFile> profDir; + rv = NS_GetSpecialDirectory(NS_APP_PROFILE_DIR_STARTUP, getter_AddRefs(profDir)); + if (NS_FAILED(rv)) { + gfxVars::SetProfDirectory(nsString()); + } else { + nsAutoString path; + profDir->GetPath(path); + gfxVars::SetProfDirectory(nsString(path)); + } + + gfxUtils::RemoveShaderCacheFromDiskIfNecessary(); } if (obs) { obs->NotifyObservers(nullptr, "gfx-features-ready", nullptr); } } /* static*/ bool @@ -2621,17 +2634,20 @@ gfxPlatform::InitWebRenderConfig() NS_LITERAL_CSTRING("FEATURE_FAILURE_ANGLE_DISABLED")); } else { gfxVars::SetUseWebRenderANGLE(gfxConfig::IsEnabled(Feature::WEBRENDER)); } } #endif if (Preferences::GetBool("gfx.webrender.program-binary", false)) { - gfx::gfxVars::SetUseWebRenderProgramBinary(gfxConfig::IsEnabled(Feature::WEBRENDER)); + gfxVars::SetUseWebRenderProgramBinary(gfxConfig::IsEnabled(Feature::WEBRENDER)); + if (Preferences::GetBool("gfx.webrender.program-binary-disk", false)) { + gfxVars::SetUseWebRenderProgramBinaryDisk(gfxConfig::IsEnabled(Feature::WEBRENDER)); + } } #ifdef MOZ_WIDGET_ANDROID featureWebRender.ForceDisable( FeatureStatus::Unavailable, "WebRender not ready for use on Android", NS_LITERAL_CSTRING("FEATURE_FAILURE_ANDROID")); #endif
--- a/gfx/thebes/gfxUtils.cpp +++ b/gfx/thebes/gfxUtils.cpp @@ -17,21 +17,24 @@ #include "mozilla/dom/ImageEncoder.h" #include "mozilla/dom/WorkerPrivate.h" #include "mozilla/dom/WorkerRunnable.h" #include "mozilla/gfx/2D.h" #include "mozilla/gfx/DataSurfaceHelpers.h" #include "mozilla/gfx/Logging.h" #include "mozilla/gfx/PathHelpers.h" #include "mozilla/gfx/Swizzle.h" +#include "mozilla/gfx/gfxVars.h" #include "mozilla/Maybe.h" #include "mozilla/RefPtr.h" #include "mozilla/UniquePtrExtensions.h" #include "mozilla/Unused.h" #include "mozilla/Vector.h" +#include "mozilla/webrender/webrender_ffi.h" +#include "nsAppRunner.h" #include "nsComponentManagerUtils.h" #include "nsIClipboardHelper.h" #include "nsIFile.h" #include "nsIGfxInfo.h" #include "nsIPresShell.h" #include "nsPresContext.h" #include "nsRegion.h" #include "nsServiceManagerUtils.h" @@ -1463,16 +1466,64 @@ gfxUtils::ThreadSafeGetFeatureStatus(con } return runnable->GetNSResult(); } return gfxInfo->GetFeatureStatus(feature, failureId, status); } +#define GFX_SHADER_CHECK_BUILD_VERSION_PREF "gfx-shader-check.build-version" +#define GFX_SHADER_CHECK_DEVICE_ID_PREF "gfx-shader-check.device-id" +#define GFX_SHADER_CHECK_DRIVER_VERSION_PREF "gfx-shader-check.driver-version" + +/* static */ void +gfxUtils::RemoveShaderCacheFromDiskIfNecessary() +{ + if (!gfxVars::UseWebRenderProgramBinaryDisk()) { + return; + } + + nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo(); + + // Get current values + nsCString buildID(mozilla::PlatformBuildID()); + nsString deviceID, driverVersion; + gfxInfo->GetAdapterDeviceID(deviceID); + gfxInfo->GetAdapterDriverVersion(driverVersion); + + // Get pref stored values + nsAutoCString buildIDChecked; + Preferences::GetCString(GFX_SHADER_CHECK_BUILD_VERSION_PREF, buildIDChecked); + nsAutoString deviceIDChecked, driverVersionChecked; + Preferences::GetString(GFX_SHADER_CHECK_DEVICE_ID_PREF, deviceIDChecked); + Preferences::GetString(GFX_SHADER_CHECK_DRIVER_VERSION_PREF, driverVersionChecked); + + if (buildID == buildIDChecked && + deviceID == deviceIDChecked && + driverVersion == driverVersionChecked) { + return; + } + + nsAutoString path(gfx::gfxVars::ProfDirectory()); + + if (!wr::remove_program_binary_disk_cache(&path)) { + // Failed to remove program binary disk cache. The disk cache might have + // invalid data. Disable program binary disk cache usage. + gfxVars::SetUseWebRenderProgramBinaryDisk(false); + return; + } + + Preferences::SetCString(GFX_SHADER_CHECK_BUILD_VERSION_PREF, buildID); + Preferences::SetString(GFX_SHADER_CHECK_DEVICE_ID_PREF, deviceID); + Preferences::SetString(GFX_SHADER_CHECK_DRIVER_VERSION_PREF, driverVersion); + return; +} + + /* static */ bool gfxUtils::DumpDisplayList() { return gfxPrefs::LayoutDumpDisplayList() || (gfxPrefs::LayoutDumpDisplayListParent() && XRE_IsParentProcess()) || (gfxPrefs::LayoutDumpDisplayListContent() && XRE_IsContentProcess()); } FILE *gfxUtils::sDumpPaintFile = stderr;
--- a/gfx/thebes/gfxUtils.h +++ b/gfx/thebes/gfxUtils.h @@ -290,16 +290,18 @@ public: const char16_t* aEncoderOptions, nsIInputStream** outStream); static nsresult ThreadSafeGetFeatureStatus(const nsCOMPtr<nsIGfxInfo>& gfxInfo, int32_t feature, nsACString& failureId, int32_t* status); + static void RemoveShaderCacheFromDiskIfNecessary(); + /** * Copy to the clipboard as a PNG encoded Data URL. */ static void CopyAsDataURI(SourceSurface* aSourceSurface); static void CopyAsDataURI(DrawTarget* aDT); static bool DumpDisplayList();
--- a/gfx/webrender_bindings/Cargo.toml +++ b/gfx/webrender_bindings/Cargo.toml @@ -6,22 +6,26 @@ license = "MPL-2.0" [dependencies] rayon = "1" thread_profiler = "0.1.1" euclid = { version = "0.17", features = ["serde"] } app_units = "0.6" gleam = "0.5" log = "0.4" +nsstring = { path = "../../servo/support/gecko/nsstring" } +bincode = "1.0" +uuid = {version = "0.1.18"} +fxhash = "0.2.1" [dependencies.webrender] path = "../webrender" version = "0.57.2" default-features = false -features = ["capture"] +features = ["capture", "serialize_program"] [target.'cfg(target_os = "windows")'.dependencies] dwrote = "0.4.1" [target.'cfg(target_os = "macos")'.dependencies] core-foundation = "0.5" core-graphics = "0.13" foreign-types = "0.3.0"
--- a/gfx/webrender_bindings/RenderThread.cpp +++ b/gfx/webrender_bindings/RenderThread.cpp @@ -65,16 +65,26 @@ RenderThread::Start() return; } sRenderThread = new RenderThread(thread); #ifdef XP_WIN widget::WinCompositorWindowThread::Start(); #endif layers::SharedSurfacesParent::Initialize(); + + if (XRE_IsGPUProcess() && + gfx::gfxVars::UseWebRenderProgramBinary()) { + MOZ_ASSERT(gfx::gfxVars::UseWebRender()); + // Initialize program cache if necessary + RefPtr<Runnable> runnable = WrapRunnable( + RefPtr<RenderThread>(sRenderThread.get()), + &RenderThread::ProgramCacheTask); + sRenderThread->Loop()->PostTask(runnable.forget()); + } } // static void RenderThread::ShutDown() { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(sRenderThread); @@ -506,40 +516,53 @@ RenderThread::GetRenderTexture(wr::WrExt { MOZ_ASSERT(IsInRenderThread()); MutexAutoLock lock(mRenderTextureMapLock); MOZ_ASSERT(mRenderTextures.GetWeak(aExternalImageId.mHandle)); return mRenderTextures.GetWeak(aExternalImageId.mHandle); } +void +RenderThread::ProgramCacheTask() +{ + ProgramCache(); +} + WebRenderProgramCache* RenderThread::ProgramCache() { MOZ_ASSERT(IsInRenderThread()); if (!mProgramCache) { - mProgramCache = MakeUnique<WebRenderProgramCache>(); + mProgramCache = MakeUnique<WebRenderProgramCache>(ThreadPool().Raw()); } return mProgramCache.get(); } WebRenderThreadPool::WebRenderThreadPool() { mThreadPool = wr_thread_pool_new(); } WebRenderThreadPool::~WebRenderThreadPool() { wr_thread_pool_delete(mThreadPool); } -WebRenderProgramCache::WebRenderProgramCache() +WebRenderProgramCache::WebRenderProgramCache(wr::WrThreadPool* aThreadPool) { - mProgramCache = wr_program_cache_new(); + MOZ_ASSERT(aThreadPool); + + nsAutoString path; + if (gfxVars::UseWebRenderProgramBinaryDisk()) { + path.Append(gfx::gfxVars::ProfDirectory()); + } + mProgramCache = wr_program_cache_new(&path, aThreadPool); + wr_try_load_shader_from_disk(mProgramCache); } WebRenderProgramCache::~WebRenderProgramCache() { wr_program_cache_delete(mProgramCache); } } // namespace wr
--- a/gfx/webrender_bindings/RenderThread.h +++ b/gfx/webrender_bindings/RenderThread.h @@ -37,17 +37,17 @@ public: wr::WrThreadPool* Raw() { return mThreadPool; } protected: wr::WrThreadPool* mThreadPool; }; class WebRenderProgramCache { public: - WebRenderProgramCache(); + explicit WebRenderProgramCache(wr::WrThreadPool* aThreadPool); ~WebRenderProgramCache(); wr::WrProgramCache* Raw() { return mProgramCache; } protected: wr::WrProgramCache* mProgramCache; }; @@ -165,16 +165,17 @@ public: /// Can only be called from the render thread. WebRenderProgramCache* ProgramCache(); private: explicit RenderThread(base::Thread* aThread); void DeferredRenderTextureHostDestroy(RefPtr<RenderTextureHost> aTexture); void ShutDownTask(layers::SynchronousTask* aTask); + void ProgramCacheTask(); ~RenderThread(); base::Thread* const mThread; WebRenderThreadPool mThreadPool; UniquePtr<WebRenderProgramCache> mProgramCache;
--- a/gfx/webrender_bindings/src/bindings.rs +++ b/gfx/webrender_bindings/src/bindings.rs @@ -8,22 +8,24 @@ use std::os::raw::{c_void, c_char, c_flo use gleam::gl; use webrender::api::*; use webrender::{ReadPixelsFormat, Renderer, RendererOptions, ThreadListener}; use webrender::{ExternalImage, ExternalImageHandler, ExternalImageSource}; use webrender::DebugFlags; use webrender::{ApiRecordingReceiver, BinaryRecorder}; use webrender::{AsyncPropertySampler, PipelineInfo, SceneBuilderHooks}; -use webrender::{ProgramCache, UploadMethod, VertexUsageHint}; +use webrender::{UploadMethod, VertexUsageHint}; use thread_profiler::register_thread_with_profiler; use moz2d_renderer::Moz2dImageRenderer; +use program_cache::{WrProgramCache, remove_disk_cache}; use app_units::Au; use rayon; use euclid::SideOffsets2D; +use nsstring::nsAString; #[cfg(target_os = "windows")] use dwrote::{FontDescriptor, FontWeight, FontStretch, FontStyle}; #[cfg(target_os = "macos")] use core_foundation::string::CFString; #[cfg(target_os = "macos")] use core_graphics::font::CGFont; @@ -822,33 +824,50 @@ pub unsafe extern "C" fn wr_thread_pool_ } /// cbindgen:postfix=WR_DESTRUCTOR_SAFE_FUNC #[no_mangle] pub unsafe extern "C" fn wr_thread_pool_delete(thread_pool: *mut WrThreadPool) { Box::from_raw(thread_pool); } -pub struct WrProgramCache(Rc<ProgramCache>); - #[no_mangle] -pub unsafe extern "C" fn wr_program_cache_new() -> *mut WrProgramCache { - let program_cache = ProgramCache::new(None); - Box::into_raw(Box::new(WrProgramCache(program_cache))) +pub unsafe extern "C" fn wr_program_cache_new(prof_path: &nsAString, thread_pool: *mut WrThreadPool) -> *mut WrProgramCache { + let workers = &(*thread_pool).0; + let program_cache = WrProgramCache::new(prof_path, workers); + Box::into_raw(Box::new(program_cache)) } /// cbindgen:postfix=WR_DESTRUCTOR_SAFE_FUNC #[no_mangle] pub unsafe extern "C" fn wr_program_cache_delete(program_cache: *mut WrProgramCache) { - Rc::from_raw(program_cache); + Box::from_raw(program_cache); +} + +#[no_mangle] +pub unsafe extern "C" fn wr_try_load_shader_from_disk(program_cache: *mut WrProgramCache) { + if !program_cache.is_null() { + (*program_cache).try_load_from_disk(); + } +} + +#[no_mangle] +pub unsafe extern "C" fn remove_program_binary_disk_cache(prof_path: &nsAString) -> bool { + match remove_disk_cache(prof_path) { + Ok(_) => true, + Err(_) => { + error!("Failed to remove program binary disk cache"); + false + } + } } #[no_mangle] pub extern "C" fn wr_renderer_update_program_cache(renderer: &mut Renderer, program_cache: &mut WrProgramCache) { - let program_cache = Rc::clone(&program_cache.0); + let program_cache = Rc::clone(&program_cache.rc_get()); renderer.update_program_cache(program_cache); } // Call MakeCurrent before this. #[no_mangle] pub extern "C" fn wr_window_new(window_id: WrWindowId, window_width: u32, window_height: u32,
--- a/gfx/webrender_bindings/src/lib.rs +++ b/gfx/webrender_bindings/src/lib.rs @@ -3,27 +3,34 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #![deny(warnings)] extern crate webrender; extern crate euclid; extern crate app_units; extern crate gleam; +extern crate nsstring; extern crate rayon; extern crate thread_profiler; +extern crate bincode; +extern crate uuid; +extern crate fxhash; #[macro_use] extern crate log; #[cfg(target_os = "windows")] extern crate dwrote; + #[cfg(target_os = "macos")] extern crate core_foundation; #[cfg(target_os = "macos")] extern crate core_graphics; #[cfg(target_os = "macos")] extern crate foreign_types; +mod program_cache; + #[allow(non_snake_case)] pub mod bindings; pub mod moz2d_renderer;
new file mode 100644 --- /dev/null +++ b/gfx/webrender_bindings/src/program_cache.rs @@ -0,0 +1,298 @@ +use std::cell::RefCell; +use std::io::{Error, ErrorKind}; +use std::fs::{File, create_dir_all, read_dir}; +use std::io::{Read, Write}; +use std::path::{PathBuf}; +use std::rc::Rc; +use std::sync::Arc; + +use webrender::{ProgramBinary, ProgramCache, ProgramCacheObserver}; +use bincode; +use fxhash; +use nsstring::nsAString; +use rayon::ThreadPool; +use uuid::Uuid; + +const MAX_LOAD_TIME_MS: u64 = 400; +const MAX_CACHED_PROGRAM_COUNT: u32 = 15; + +fn deserialize_program_binary(path: &PathBuf) -> Result<Arc<ProgramBinary>, Error> { + let mut buf = vec![]; + let mut file = File::open(path)?; + file.read_to_end(&mut buf)?; + + if buf.len() <= 8 { + return Err(Error::new(ErrorKind::InvalidData, "File size is too small")); + } + let hash = &buf[0 .. 8]; + let data = &buf[8 ..]; + + // Check if hash is correct + let hash:u64 = bincode::deserialize(&hash).unwrap(); + let hash_data = fxhash::hash64(&data); + if hash != hash_data { + return Err(Error::new(ErrorKind::InvalidData, "File data is invalid")); + } + + // Deserialize ProgramBinary + let binary = match bincode::deserialize(&data) { + Ok(binary) => binary, + Err(_) => return Err(Error::new(ErrorKind::InvalidData, "Failed to deserialize ProgramBinary")), + }; + + Ok(Arc::new(binary)) +} + +#[cfg(target_os = "windows")] +fn get_cache_path_from_prof_path(prof_path: &nsAString) -> Option<PathBuf> { + if prof_path.is_empty() { + // Empty means that we do not use disk cache. + return None; + } + + use std::ffi::OsString; + use std::os::windows::prelude::*; + + let prof_path = OsString::from_wide(prof_path.as_ref()); + let mut cache_path = PathBuf::from(&prof_path); + cache_path.push("shader-cache"); + + Some(cache_path) +} + +#[cfg(not(target_os="windows"))] +fn get_cache_path_from_prof_path(_prof_path: &nsAString) -> Option<PathBuf> { + // Not supported yet. + None +} + +struct WrProgramBinaryDiskCache { + cache_path: Option<PathBuf>, + program_count: u32, + is_enabled: bool, + workers: Arc<ThreadPool>, +} + +impl WrProgramBinaryDiskCache { + #[allow(dead_code)] + fn new(prof_path: &nsAString, workers: &Arc<ThreadPool>) -> Self { + let cache_path = get_cache_path_from_prof_path(prof_path); + let is_enabled = cache_path.is_some(); + let workers = Arc::clone(workers); + + WrProgramBinaryDiskCache{ + cache_path, + program_count: 0, + is_enabled, + workers, + } + } + + fn notify_binary_added(&mut self, program_binary: &Arc<ProgramBinary>) { + if !self.is_enabled { + return; + } + + if let Some(ref cache_path) = self.cache_path { + if let Err(_) = create_dir_all(&cache_path) { + error!("failed to create dir for shader disk cache"); + return; + } + + self.program_count += 1; + if self.program_count > MAX_CACHED_PROGRAM_COUNT { + // Disable disk cache to avoid storing more shader programs to disk + self.is_enabled = false; + return; + } + + // Use uuid for file name + let uuid1 = Uuid::new_v4(); + let file_name = uuid1.to_hyphenated_string(); + let program_binary = Arc::clone(program_binary); + let file_path = cache_path.join(&file_name); + + let program_count = self.program_count; + + // Save to disk on worker thread + self.workers.spawn(move || { + + use std::time::{Instant}; + let start = Instant::now(); + + let data: Vec<u8> = match bincode::serialize(&*program_binary) { + Ok(data) => data, + Err(err) => { + error!("Failed to serialize program binary error: {}", err); + return; + } + }; + + let mut file = match File::create(&file_path) { + Ok(file) => file, + Err(err) => { + error!("Unable to create file for program binary error: {}", err); + return; + } + }; + + // Write hash + let hash = fxhash::hash64(&data); + let hash = bincode::serialize(&hash).unwrap(); + assert!(hash.len() == 8); + match file.write_all(&hash) { + Err(err) => { + error!("Failed to write hash to file error: {}", err); + } + _ => {}, + }; + + // Write serialized data + match file.write_all(&data) { + Err(err) => { + error!("Failed to write program binary to file error: {}", err); + } + _ => {}, + }; + + let elapsed = start.elapsed(); + info!("notify_binary_added: {} ms program_count {}", + (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64, program_count); + + }); + } + } + + pub fn try_load_from_disk(&mut self, program_cache: &Rc<ProgramCache>) { + if !self.is_enabled { + return; + } + + if let Some(ref cache_path) = self.cache_path { + use std::time::{Instant}; + let start = Instant::now(); + + // Load program binaries if exist + if cache_path.exists() && cache_path.is_dir() { + for entry in read_dir(cache_path).unwrap() { + let entry = entry.unwrap(); + let path = entry.path(); + + info!("loading shader file"); + + match deserialize_program_binary(&path) { + Ok(program) => { + program_cache.load_program_binary(program); + } + Err(err) => { + error!("Failed to desriralize program binary error: {}", err); + } + }; + + self.program_count += 1; + + let elapsed = start.elapsed(); + let elapsed_ms = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64; + info!("deserialize_program_binary: {} ms program_count {}", elapsed_ms, self.program_count); + + if self.program_count > MAX_CACHED_PROGRAM_COUNT || elapsed_ms > MAX_LOAD_TIME_MS { + // Disable disk cache to avoid storing more shader programs to disk + self.is_enabled = false; + break; + } + } + } + } + } +} + +pub struct WrProgramCacheObserver { + disk_cache: Rc<RefCell<WrProgramBinaryDiskCache>>, +} + +impl WrProgramCacheObserver { + #[allow(dead_code)] + fn new(disk_cache: Rc<RefCell<WrProgramBinaryDiskCache>>) -> Self { + WrProgramCacheObserver{ + disk_cache, + } + } +} + +impl ProgramCacheObserver for WrProgramCacheObserver { + fn notify_binary_added(&self, program_binary: &Arc<ProgramBinary>) { + self.disk_cache.borrow_mut().notify_binary_added(program_binary); + } + + fn notify_program_binary_failed(&self, _program_binary: &Arc<ProgramBinary>) { + error!("Failed program_binary"); + } +} + + +pub struct WrProgramCache { + program_cache: Rc<ProgramCache>, + disk_cache: Option<Rc<RefCell<WrProgramBinaryDiskCache>>>, +} + +impl WrProgramCache { + #[cfg(target_os = "windows")] + pub fn new(prof_path: &nsAString, workers: &Arc<ThreadPool>) -> Self { + let disk_cache = Rc::new(RefCell::new(WrProgramBinaryDiskCache::new(prof_path, workers))); + let program_cache_observer = Box::new(WrProgramCacheObserver::new(Rc::clone(&disk_cache))); + let program_cache = ProgramCache::new(Some(program_cache_observer)); + + WrProgramCache { + program_cache, + disk_cache: Some(disk_cache), + } + } + + #[cfg(not(target_os="windows"))] + pub fn new(_prof_path: &nsAString, _: &Arc<ThreadPool>) -> Self { + let program_cache = ProgramCache::new(None); + + WrProgramCache { + program_cache, + disk_cache: None, + } + } + + pub fn rc_get(&self) -> &Rc<ProgramCache> { + &self.program_cache + } + + pub fn try_load_from_disk(&self) { + if let Some(ref disk_cache) = self.disk_cache { + disk_cache.borrow_mut().try_load_from_disk(&self.program_cache); + } else { + error!("Shader disk cache is not supported"); + } + } +} + +#[cfg(target_os = "windows")] +pub fn remove_disk_cache(prof_path: &nsAString) -> Result<(), Error> { + use std::fs::remove_dir_all; + use std::time::{Instant}; + + if let Some(cache_path) = get_cache_path_from_prof_path(prof_path) { + if cache_path.exists() { + let start = Instant::now(); + + remove_dir_all(&cache_path)?; + + let elapsed = start.elapsed(); + let elapsed_ms = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64; + info!("remove_disk_cache: {} ms", elapsed_ms); + } + } + Ok(()) +} + +#[cfg(not(target_os="windows"))] +pub fn remove_disk_cache(_prof_path: &nsAString) -> Result<(), Error> { + error!("Shader disk cache is not supported"); + return Err(Error::new(ErrorKind::Other, "Not supported")) +} +
--- a/gfx/webrender_bindings/webrender_ffi_generated.h +++ b/gfx/webrender_bindings/webrender_ffi_generated.h @@ -12,16 +12,20 @@ */ #include <cstdint> #include <cstdlib> namespace mozilla { namespace wr { +static const uint32_t MAX_CACHED_PROGRAM_COUNT = 15; + +static const uint64_t MAX_LOAD_TIME_MS = 400; + enum class BorderStyle : uint32_t { None = 0, Solid = 1, Double = 2, Dotted = 3, Dashed = 4, Hidden = 5, Groove = 6, @@ -996,16 +1000,20 @@ extern bool is_glcontext_egl(void *aGlco extern bool is_in_compositor_thread(); extern bool is_in_main_thread(); extern bool is_in_render_thread(); WR_INLINE +bool remove_program_binary_disk_cache(const nsAString *aProfPath) +WR_FUNC; + +WR_INLINE const VecU8 *wr_add_ref_arc(const ArcVecU8 *aArc) WR_FUNC; WR_INLINE void wr_api_capture(DocumentHandle *aDh, const char *aPath, uint32_t aBitsRaw) WR_FUNC; @@ -1406,17 +1414,18 @@ WR_INLINE void wr_pipeline_info_delete(WrPipelineInfo aInfo) WR_DESTRUCTOR_SAFE_FUNC; WR_INLINE void wr_program_cache_delete(WrProgramCache *aProgramCache) WR_DESTRUCTOR_SAFE_FUNC; WR_INLINE -WrProgramCache *wr_program_cache_new() +WrProgramCache *wr_program_cache_new(const nsAString *aProfPath, + WrThreadPool *aThreadPool) WR_FUNC; WR_INLINE bool wr_renderer_current_epoch(Renderer *aRenderer, WrPipelineId aPipelineId, WrEpoch *aOutEpoch) WR_FUNC; @@ -1658,16 +1667,20 @@ WR_FUNC; WR_INLINE void wr_transaction_update_epoch(Transaction *aTxn, WrPipelineId aPipelineId, WrEpoch aEpoch) WR_FUNC; WR_INLINE +void wr_try_load_shader_from_disk(WrProgramCache *aProgramCache) +WR_FUNC; + +WR_INLINE void wr_vec_u8_free(WrVecU8 aV) WR_FUNC; WR_INLINE void wr_vec_u8_push_bytes(WrVecU8 *aV, ByteSlice aBytes) WR_FUNC;
--- a/ipc/glue/IPCStream.ipdlh +++ b/ipc/glue/IPCStream.ipdlh @@ -29,16 +29,18 @@ struct IPCRemoteStream { // If this is true, the stream will send data only when the first operation // is done on the destination side. The benefit of this is that we do not // send data if not needed. On the other hand, it could have a performance // issue. bool delayedStart; IPCRemoteStreamType stream; + + int64_t length; }; // Use IPCStream or OptionalIPCStream in your ipdl to represent serialized // nsIInputStreams. Then use AutoIPCStream from IPCStreamUtils.h to perform // the serialization. union IPCStream { InputStreamParamsWithFds;
--- a/ipc/glue/IPCStreamDestination.cpp +++ b/ipc/glue/IPCStreamDestination.cpp @@ -1,15 +1,16 @@ /* -*- 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 "IPCStreamDestination.h" +#include "mozilla/InputStreamLengthWrapper.h" #include "mozilla/Mutex.h" #include "nsIAsyncInputStream.h" #include "nsIAsyncOutputStream.h" #include "nsIBufferedStreams.h" #include "nsICloneableInputStream.h" #include "nsIPipe.h" namespace mozilla { @@ -259,16 +260,19 @@ NS_INTERFACE_MAP_BEGIN(IPCStreamDestinat NS_INTERFACE_MAP_END // ---------------------------------------------------------------------------- // IPCStreamDestination IPCStreamDestination::IPCStreamDestination() : mOwningThread(NS_GetCurrentThread()) , mDelayedStart(false) +#ifdef MOZ_DEBUG + , mLengthSet(false) +#endif { } IPCStreamDestination::~IPCStreamDestination() { } nsresult @@ -297,16 +301,34 @@ IPCStreamDestination::Initialize() } void IPCStreamDestination::SetDelayedStart(bool aDelayedStart) { mDelayedStart = aDelayedStart; } +void +IPCStreamDestination::SetLength(int64_t aLength) +{ + MOZ_ASSERT(mReader); + MOZ_ASSERT(!mLengthSet); + +#ifdef DEBUG + mLengthSet = true; +#endif + + if (aLength != -1) { + nsCOMPtr<nsIInputStream> finalStream; + finalStream = new InputStreamLengthWrapper(mReader.forget(), aLength); + mReader = do_QueryInterface(finalStream); + MOZ_ASSERT(mReader); + } +} + already_AddRefed<nsIInputStream> IPCStreamDestination::TakeReader() { MOZ_ASSERT(mReader); MOZ_ASSERT(!mDelayedStartInputStream); if (mDelayedStart) { mDelayedStartInputStream =
--- a/ipc/glue/IPCStreamDestination.h +++ b/ipc/glue/IPCStreamDestination.h @@ -34,16 +34,19 @@ public: Cast(PChildToParentStreamParent* aActor); static IPCStreamDestination* Cast(PParentToChildStreamChild* aActor); void SetDelayedStart(bool aDelayedStart); + void + SetLength(int64_t aLength); + already_AddRefed<nsIInputStream> TakeReader(); bool IsOnOwningThread() const; void DispatchRunnable(already_AddRefed<nsIRunnable>&& aRunnable); @@ -91,14 +94,18 @@ private: // This is created by TakeReader() if we need to delay the reading of data. // We keep a reference to the stream in order to inform it when the actor goes // away. If that happens, the reading of data will not be possible anymore. class DelayedStartInputStream; RefPtr<DelayedStartInputStream> mDelayedStartInputStream; nsCOMPtr<nsIThread> mOwningThread; bool mDelayedStart; + +#ifdef MOZ_DEBUG + bool mLengthSet; +#endif }; } // namespace ipc } // namespace mozilla #endif // mozilla_ipc_IPCStreamDestination_h
--- a/ipc/glue/IPCStreamUtils.cpp +++ b/ipc/glue/IPCStreamUtils.cpp @@ -4,16 +4,17 @@ * 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 "IPCStreamUtils.h" #include "nsIIPCSerializableInputStream.h" #include "mozilla/Assertions.h" +#include "mozilla/InputStreamLengthHelper.h" #include "mozilla/dom/ContentChild.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/File.h" #include "mozilla/ipc/FileDescriptorSetChild.h" #include "mozilla/ipc/FileDescriptorSetParent.h" #include "mozilla/ipc/InputStreamUtils.h" #include "mozilla/ipc/IPCStreamDestination.h" #include "mozilla/ipc/IPCStreamSource.h" @@ -132,16 +133,24 @@ SerializeInputStreamWithFdsParent(nsIIPC template<typename M> bool SerializeInputStream(nsIInputStream* aStream, IPCStream& aValue, M* aManager, bool aDelayedStart) { MOZ_ASSERT(aStream); MOZ_ASSERT(aManager); + // Let's try to take the length using InputStreamLengthHelper. If the length + // cannot be taken synchronously, and its length is needed, the stream needs + // to be fully copied in memory on the deserialization side. + int64_t length; + if (!InputStreamLengthHelper::GetSyncLength(aStream, &length)) { + length = -1; + } + // As a fallback, attempt to stream the data across using a IPCStream // actor. For blocking streams, create a nonblocking pipe instead, nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(aStream); if (!asyncStream) { const uint32_t kBufferSize = 32768; // matches IPCStream buffer size. nsCOMPtr<nsIAsyncOutputStream> sink; nsresult rv = NS_NewPipe2(getter_AddRefs(asyncStream), getter_AddRefs(sink), @@ -163,16 +172,17 @@ SerializeInputStream(nsIInputStream* aSt } } MOZ_ASSERT(asyncStream); IPCRemoteStream remoteStream; remoteStream.delayedStart() = aDelayedStart; remoteStream.stream() = IPCStreamSource::Create(asyncStream, aManager); + remoteStream.length() = length; aValue = remoteStream; return true; } template<typename M> bool SerializeInputStreamChild(nsIInputStream* aStream, M* aManager, @@ -374,16 +384,17 @@ DeserializeIPCStream(const IPCStream& aV IPCStreamDestination::Cast(remoteStreamType.get_PChildToParentStreamParent()); } else { MOZ_ASSERT(remoteStreamType.type() == IPCRemoteStreamType::TPParentToChildStreamChild); destinationStream = IPCStreamDestination::Cast(remoteStreamType.get_PParentToChildStreamChild()); } destinationStream->SetDelayedStart(remoteStream.delayedStart()); + destinationStream->SetLength(remoteStream.length()); return destinationStream->TakeReader(); } // Note, we explicitly do not support deserializing the PChildToParentStream actor on // the child side nor the PParentToChildStream actor on the parent side. MOZ_ASSERT(aValue.type() == IPCStream::TInputStreamParamsWithFds); const InputStreamParamsWithFds& streamWithFds =
--- a/ipc/glue/InputStreamParams.ipdlh +++ b/ipc/glue/InputStreamParams.ipdlh @@ -56,16 +56,17 @@ union InputStreamParams { StringInputStreamParams; FileInputStreamParams; BufferedInputStreamParams; MIMEInputStreamParams; MultiplexInputStreamParams; SlicedInputStreamParams; IPCBlobInputStreamParams; + InputStreamLengthWrapperParams; }; union OptionalInputStreamParams { void_t; InputStreamParams; }; @@ -77,10 +78,17 @@ struct BufferedInputStreamParams struct MIMEInputStreamParams { OptionalInputStreamParams optionalStream; HeaderEntry[] headers; bool startedReading; }; +struct InputStreamLengthWrapperParams +{ + InputStreamParams stream; + int64_t length; + bool consumed; +}; + } // namespace ipc } // namespace mozilla
--- a/ipc/glue/InputStreamUtils.cpp +++ b/ipc/glue/InputStreamUtils.cpp @@ -8,16 +8,17 @@ #include "nsIIPCSerializableInputStream.h" #include "mozilla/Assertions.h" #include "mozilla/dom/File.h" #include "mozilla/dom/ipc/IPCBlobInputStream.h" #include "mozilla/dom/ipc/IPCBlobInputStreamStorage.h" #include "mozilla/SlicedInputStream.h" +#include "mozilla/InputStreamLengthWrapper.h" #include "nsComponentManagerUtils.h" #include "nsDebug.h" #include "nsID.h" #include "nsIXULRuntime.h" #include "nsMIMEInputStream.h" #include "nsMultiplexInputStream.h" #include "nsNetCID.h" #include "nsStringStream.h" @@ -95,16 +96,20 @@ InputStreamHelper::DeserializeInputStrea case InputStreamParams::TMultiplexInputStreamParams: serializable = do_CreateInstance(kMultiplexInputStreamCID); break; case InputStreamParams::TSlicedInputStreamParams: serializable = new mozilla::SlicedInputStream(); break; + case InputStreamParams::TInputStreamLengthWrapperParams: + serializable = new mozilla::InputStreamLengthWrapper(); + break; + default: MOZ_ASSERT(false, "Unknown params!"); return nullptr; } MOZ_ASSERT(serializable); if (!serializable->Deserialize(aParams, aFileDescriptors)) {
--- a/js/public/Realm.h +++ b/js/public/Realm.h @@ -92,18 +92,18 @@ SetDestroyRealmCallback(JSContext* cx, D typedef void (* RealmNameCallback)(JSContext* cx, Handle<Realm*> realm, char* buf, size_t bufsize); // Set the callback SpiderMonkey calls to get the name of a realm, for // diagnostic output. extern JS_PUBLIC_API(void) SetRealmNameCallback(JSContext* cx, RealmNameCallback callback); -// Get the global object for the given realm. Returns null only if `realm` is -// the atoms realm. +// Get the global object for the given realm. This only returns nullptr during +// GC, between collecting the global object and destroying the Realm. extern JS_PUBLIC_API(JSObject*) GetRealmGlobalOrNull(Handle<Realm*> realm); extern JS_PUBLIC_API(JSObject*) GetRealmObjectPrototype(JSContext* cx); extern JS_PUBLIC_API(JSObject*) GetRealmFunctionPrototype(JSContext* cx);
--- a/js/public/RootingAPI.h +++ b/js/public/RootingAPI.h @@ -879,17 +879,17 @@ class RootingContext static const RootingContext* get(const JSContext* cx) { return reinterpret_cast<const RootingContext*>(cx); } static RootingContext* get(JSContext* cx) { return reinterpret_cast<RootingContext*>(cx); } - friend JSCompartment* js::GetContextCompartment(const JSContext* cx); + friend JS::Realm* js::GetContextRealm(const JSContext* cx); friend JS::Zone* js::GetContextZone(const JSContext* cx); }; class JS_PUBLIC_API(AutoGCRooter) { protected: enum class Tag : uint8_t { Array, /* js::AutoArrayRooter */ @@ -1049,20 +1049,26 @@ namespace js { * * - These must not be available on the more restricted superclasses of * JSContext, so we can't simply define them on RootingContext. * * - They're perfectly ordinary JSContext functionality, so ought to be * usable without resorting to jsfriendapi.h, and when JSContext is an * incomplete type. */ +inline JS::Realm* +GetContextRealm(const JSContext* cx) +{ + return JS::RootingContext::get(cx)->realm_; +} + inline JSCompartment* GetContextCompartment(const JSContext* cx) { - return GetCompartmentForRealm(JS::RootingContext::get(cx)->realm_); + return GetCompartmentForRealm(GetContextRealm(cx)); } inline JS::Zone* GetContextZone(const JSContext* cx) { return JS::RootingContext::get(cx)->zone_; }
--- a/js/public/Wrapper.h +++ b/js/public/Wrapper.h @@ -373,19 +373,16 @@ UnwrapOneChecked(JSObject* obj, bool sto // ExposeToActiveJS is not called on wrapper targets so this can be called from // the GC or off the main thread. JS_FRIEND_API(JSObject*) UncheckedUnwrapWithoutExpose(JSObject* obj); void ReportAccessDenied(JSContext* cx); -JS_FRIEND_API(bool) -IsCrossCompartmentWrapper(JSObject* obj); - JS_FRIEND_API(void) NukeCrossCompartmentWrapper(JSContext* cx, JSObject* wrapper); void RemapWrapper(JSContext* cx, JSObject* wobj, JSObject* newTarget); JS_FRIEND_API(bool) RemapAllWrappersForObject(JSContext* cx, JSObject* oldTarget,
--- a/js/rust/build.rs +++ b/js/rust/build.rs @@ -169,16 +169,17 @@ const WHITELIST_TYPES: &'static [&'stati "JS::HandleString", "JS::HandleValue", "JS::HandleValueArray", "JS::IsAcceptableThis", "JSAutoRealm", "JSAutoStructuredCloneBuffer", "JSClass", "JSClassOps", + "JSCompartment", "JSContext", "JSErrNum", "JSErrorCallback", "JSErrorFormatString", "JSErrorReport", "JSExnType", "JSFlatString", "JSFunction",
--- a/js/rust/src/rust.rs +++ b/js/rust/src/rust.rs @@ -1050,17 +1050,17 @@ unsafe fn get_object_group(obj: *mut JSO #[inline] pub unsafe fn get_object_class(obj: *mut JSObject) -> *const JSClass { (*get_object_group(obj)).clasp as *const _ } #[inline] pub unsafe fn get_object_compartment(obj: *mut JSObject) -> *mut JSCompartment { - (*get_object_group(obj)).compartment + (*get_object_group(obj)).realm as *mut JSCompartment } #[inline] pub fn is_dom_class(class: &JSClass) -> bool { class.flags & JSCLASS_IS_DOMJSCLASS != 0 } #[inline]
--- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -1994,17 +1994,17 @@ CreateObjectConstructor(JSContext* cx, J fun->setJitInfo(&jit::JitInfo_Object); return fun; } static JSObject* CreateObjectPrototype(JSContext* cx, JSProtoKey key) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); MOZ_ASSERT(cx->global()->isNative()); /* * Create |Object.prototype| first, mirroring CreateBlankProto but for the * prototype of the created object. */ RootedPlainObject objectProto(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr, SingletonObject));
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1502,56 +1502,65 @@ BytecodeEmitter::TDZCheckCache::noteTDZC return true; } // Class for emitting bytecode for blocks like try-catch-finally. // // Usage: (check for the return value is omitted for simplicity) // // `try { try_block } catch (ex) { catch_block }` -// TryEmitter tryCatch(this, TryEmitter::TryCatch); +// TryEmitter tryCatch(this, TryEmitter::Kind::TryCatch, +// TryEmitter::ControlKind::Syntactic); // tryCatch.emitTry(); // emit(try_block); // tryCatch.emitCatch(); // emit(ex and catch_block); // use JSOP_EXCEPTION to get exception // tryCatch.emitEnd(); // // `try { try_block } finally { finally_block }` -// TryEmitter tryCatch(this, TryEmitter::TryFinally); +// TryEmitter tryCatch(this, TryEmitter::Kind::TryFinally, +// TryEmitter::ControlKind::Syntactic); // tryCatch.emitTry(); // emit(try_block); // // finally_pos: The "{" character's position in the source code text. // tryCatch.emitFinally(Some(finally_pos)); // emit(finally_block); // tryCatch.emitEnd(); // // `try { try_block } catch (ex) {catch_block} finally { finally_block }` -// TryEmitter tryCatch(this, TryEmitter::TryCatchFinally); +// TryEmitter tryCatch(this, TryEmitter::Kind::TryCatchFinally, +// TryEmitter::ControlKind::Syntactic); // tryCatch.emitTry(); // emit(try_block); // tryCatch.emitCatch(); // emit(ex and catch_block); // tryCatch.emitFinally(Some(finally_pos)); // emit(finally_block); // tryCatch.emitEnd(); // class MOZ_STACK_CLASS TryEmitter { public: - enum Kind { + enum class Kind { TryCatch, TryCatchFinally, TryFinally }; - // Whether the catch and finally blocks handle the frame's return value. - // If UseRetVal is specified, the bytecode marked with "*" are emitted - // to clear return value with `undefined` before the catch block and the - // finally block, and also to save/restore the return value before/after - // the finally block. + // Syntactic try-catch-finally and internally used non-syntactic + // try-catch-finally behave differently for 2 points. + // + // The first one is whether TryFinallyControl is used or not. + // See the comment for `controlInfo_`. + // + // The second one is whether the catch and finally blocks handle the frame's + // return value. For syntactic try-catch-finally, the bytecode marked with + // "*" are emitted to clear return value with `undefined` before the catch + // block and the finally block, and also to save/restore the return value + // before/after the finally block. // // JSOP_TRY // // try_body... // // JSOP_GOSUB finally // JSOP_JUMPTARGET // JSOP_GOTO end: @@ -1576,57 +1585,45 @@ class MOZ_STACK_CLASS TryEmitter // finally_body... // // * JSOP_SETRVAL // JSOP_NOP // // end: // JSOP_JUMPTARGET // - // For syntactic try-catch-finally, UseRetVal should be used. - // For non-syntactic try-catch-finally, DontUseRetVal should be used. - enum ShouldUseRetVal { - UseRetVal, - DontUseRetVal - }; - - // Whether this class should use TryFinallyControl. - // See the comment for `controlInfo_`. - // - // For syntactic try-catch-finally, UseControl should be used. - // For non-syntactic try-catch-finally, DontUseControl should be used. - enum ShouldUseControl { - UseControl, - DontUseControl, + // For syntactic try-catch-finally, Syntactic should be used. + // For non-syntactic try-catch-finally, NonSyntactic should be used. + enum class ControlKind { + Syntactic, + NonSyntactic }; private: BytecodeEmitter* bce_; Kind kind_; - ShouldUseRetVal retValKind_; + ControlKind controlKind_; // Track jumps-over-catches and gosubs-to-finally for later fixup. // // When a finally block is active, non-local jumps (including // jumps-over-catches) result in a GOSUB being written into the bytecode // stream and fixed-up later. // - // If ShouldUseControl is DontUseControl, all that handling is skipped. - // DontUseControl is used by yield* and the internal try-catch around - // IteratorClose. These internal uses must: + // For non-syntactic try-catch-finally, all that handling is skipped. + // The non-syntactic try-catch-finally must: // * have only one catch block // * have JSOP_GOTO at the end of catch-block // * have no non-local-jump // * don't use finally block for normal completion of try-block and // catch-block // - // Additionally, a finally block may be emitted when ShouldUseControl is - // DontUseControl, even if the kind is not TryCatchFinally or TryFinally, - // because GOSUBs are not emitted. This internal use shares the - // requirements as above. + // Additionally, a finally block may be emitted for non-syntactic + // try-catch-finally, even if the kind is TryCatch, because GOSUBs are not + // emitted. Maybe<TryFinallyControl> controlInfo_; // The stack depth before emitting JSOP_TRY. int depth_; // The source note index for SRC_TRY. unsigned noteIndex_; @@ -1637,78 +1634,81 @@ class MOZ_STACK_CLASS TryEmitter JumpList catchAndFinallyJump_; // The offset of JSOP_GOTO at the end of the try block. JumpTarget tryEnd_; // The offset of JSOP_JUMPTARGET at the beginning of the finally block. JumpTarget finallyStart_; +#ifdef DEBUG // The state of this emitter. // // +-------+ emitTry +-----+ emitCatch +-------+ emitEnd +-----+ // | Start |-------->| Try |-+---------->| Catch |-+->+--------->| End | // +-------+ +-----+ | +-------+ | ^ +-----+ // | | | // | +------------------+ +----+ // | | | // | v emitFinally +---------+ | // +->+------------>| Finally |--+ // +---------+ - enum State { + enum class State { // The initial state. Start, // After calling emitTry. Try, // After calling emitCatch. Catch, // After calling emitFinally. Finally, // After calling emitEnd. End }; State state_; +#endif bool hasCatch() const { - return kind_ == TryCatch || kind_ == TryCatchFinally; + return kind_ == Kind::TryCatch || kind_ == Kind::TryCatchFinally; } bool hasFinally() const { - return kind_ == TryCatchFinally || kind_ == TryFinally; + return kind_ == Kind::TryCatchFinally || kind_ == Kind::TryFinally; } public: - TryEmitter(BytecodeEmitter* bce, Kind kind, ShouldUseRetVal retValKind = UseRetVal, - ShouldUseControl controlKind = UseControl) + TryEmitter(BytecodeEmitter* bce, Kind kind, ControlKind controlKind) : bce_(bce), kind_(kind), - retValKind_(retValKind), + controlKind_(controlKind), depth_(0), noteIndex_(0), - tryStart_(0), - state_(Start) + tryStart_(0) +#ifdef DEBUG + , state_(State::Start) +#endif { - if (controlKind == UseControl) + if (controlKind_ == ControlKind::Syntactic) controlInfo_.emplace(bce_, hasFinally() ? StatementKind::Finally : StatementKind::Try); finallyStart_.offset = 0; } // Emits JSOP_GOTO to the end of try-catch-finally. // Used in `yield*`. - bool emitJumpOverCatchAndFinally() { + MOZ_MUST_USE bool emitJumpOverCatchAndFinally() { if (!bce_->emitJump(JSOP_GOTO, &catchAndFinallyJump_)) return false; return true; } - bool emitTry() { - MOZ_ASSERT(state_ == Start); + MOZ_MUST_USE bool emitTry() { + MOZ_ASSERT(state_ == State::Start); // Since an exception can be thrown at any place inside the try block, // we need to restore the stack and the scope chain before we transfer // the control to the exception handler. // // For that we store in a try note associated with the catch or // finally block the stack depth upon the try entry. The interpreter // uses this depth to properly unwind the stack and the scope chain. @@ -1716,23 +1716,25 @@ class MOZ_STACK_CLASS TryEmitter // Record the try location, then emit the try block. if (!bce_->newSrcNote(SRC_TRY, ¬eIndex_)) return false; if (!bce_->emit1(JSOP_TRY)) return false; tryStart_ = bce_->offset(); - state_ = Try; +#ifdef DEBUG + state_ = State::Try; +#endif return true; } private: - bool emitTryEnd() { - MOZ_ASSERT(state_ == Try); + MOZ_MUST_USE bool emitTryEnd() { + MOZ_ASSERT(state_ == State::Try); MOZ_ASSERT(depth_ == bce_->stackDepth); // GOSUB to finally, if present. if (hasFinally() && controlInfo_) { if (!bce_->emitJump(JSOP_GOSUB, &controlInfo_->gosubs)) return false; } @@ -1746,41 +1748,43 @@ class MOZ_STACK_CLASS TryEmitter if (!bce_->emitJumpTarget(&tryEnd_)) return false; return true; } public: - bool emitCatch() { - MOZ_ASSERT(state_ == Try); + MOZ_MUST_USE bool emitCatch() { + MOZ_ASSERT(state_ == State::Try); if (!emitTryEnd()) return false; MOZ_ASSERT(bce_->stackDepth == depth_); - if (retValKind_ == UseRetVal) { + if (controlKind_ == ControlKind::Syntactic) { // Clear the frame's return value that might have been set by the // try block: // // eval("try { 1; throw 2 } catch(e) {}"); // undefined, not 1 if (!bce_->emit1(JSOP_UNDEFINED)) return false; if (!bce_->emit1(JSOP_SETRVAL)) return false; } - state_ = Catch; +#ifdef DEBUG + state_ = State::Catch; +#endif return true; } private: - bool emitCatchEnd() { - MOZ_ASSERT(state_ == Catch); + MOZ_MUST_USE bool emitCatchEnd() { + MOZ_ASSERT(state_ == State::Catch); if (!controlInfo_) return true; // gosub <finally>, if required. if (hasFinally()) { if (!bce_->emitJump(JSOP_GOSUB, &controlInfo_->gosubs)) return false; @@ -1794,34 +1798,36 @@ class MOZ_STACK_CLASS TryEmitter return true; } public: // If `finallyPos` is specified, it's an offset of the finally block's // "{" character in the source code text, to improve line:column number in // the error reporting. // For non-syntactic try-catch-finally, `finallyPos` can be omitted. - bool emitFinally(const Maybe<uint32_t>& finallyPos = Nothing()) { + MOZ_MUST_USE bool emitFinally(const Maybe<uint32_t>& finallyPos = Nothing()) { // If we are using controlInfo_ (i.e., emitting a syntactic try // blocks), we must have specified up front if there will be a finally - // close. For internal try blocks, like those emitted for yield* and - // IteratorClose inside for-of loops, we can emitFinally even without - // specifying up front, since the internal try blocks emit no GOSUBs. + // close. For internal non-syntactic try blocks, like those emitted for + // yield* and IteratorClose inside for-of loops, we can emitFinally even + // without specifying up front, since the internal non-syntactic try + // blocks emit no GOSUBs. if (!controlInfo_) { - if (kind_ == TryCatch) - kind_ = TryCatchFinally; + if (kind_ == Kind::TryCatch) + kind_ = Kind::TryCatchFinally; } else { MOZ_ASSERT(hasFinally()); } - if (state_ == Try) { + if (!hasCatch()) { + MOZ_ASSERT(state_ == State::Try); if (!emitTryEnd()) return false; } else { - MOZ_ASSERT(state_ == Catch); + MOZ_ASSERT(state_ == State::Catch); if (!emitCatchEnd()) return false; } MOZ_ASSERT(bce_->stackDepth == depth_); if (!bce_->emitJumpTarget(&finallyStart_)) return false; @@ -1836,59 +1842,60 @@ class MOZ_STACK_CLASS TryEmitter } if (finallyPos) { if (!bce_->updateSourceCoordNotes(finallyPos.value())) return false; } if (!bce_->emit1(JSOP_FINALLY)) return false; - if (retValKind_ == UseRetVal) { + if (controlKind_ == ControlKind::Syntactic) { if (!bce_->emit1(JSOP_GETRVAL)) return false; // Clear the frame's return value to make break/continue return // correct value even if there's no other statement before them: // // eval("x: try { 1 } finally { break x; }"); // undefined, not 1 if (!bce_->emit1(JSOP_UNDEFINED)) return false; if (!bce_->emit1(JSOP_SETRVAL)) return false; } - state_ = Finally; +#ifdef DEBUG + state_ = State::Finally; +#endif return true; } private: - bool emitFinallyEnd() { - MOZ_ASSERT(state_ == Finally); - - if (retValKind_ == UseRetVal) { + MOZ_MUST_USE bool emitFinallyEnd() { + MOZ_ASSERT(state_ == State::Finally); + + if (controlKind_ == ControlKind::Syntactic) { if (!bce_->emit1(JSOP_SETRVAL)) return false; } if (!bce_->emit1(JSOP_RETSUB)) return false; bce_->hasTryFinally = true; return true; } public: - bool emitEnd() { - if (state_ == Catch) { - MOZ_ASSERT(!hasFinally()); + MOZ_MUST_USE bool emitEnd() { + if (!hasFinally()) { + MOZ_ASSERT(state_ == State::Catch); if (!emitCatchEnd()) return false; } else { - MOZ_ASSERT(state_ == Finally); - MOZ_ASSERT(hasFinally()); + MOZ_ASSERT(state_ == State::Finally); if (!emitFinallyEnd()) return false; } MOZ_ASSERT(bce_->stackDepth == depth_); // ReconstructPCStack needs a NOP here to mark the end of the last // catch block. @@ -1909,209 +1916,290 @@ class MOZ_STACK_CLASS TryEmitter // If we've got a finally, mark try+catch region with additional // trynote to catch exceptions (re)thrown from a catch block or // for the try{}finally{} case. if (hasFinally()) { if (!bce_->tryNoteList.append(JSTRY_FINALLY, depth_, tryStart_, finallyStart_.offset)) return false; } - state_ = End; +#ifdef DEBUG + state_ = State::End; +#endif return true; } }; // Class for emitting bytecode for blocks like if-then-else. // -// This class can be used to emit single if-then-else block. Cascading -// elseif's need multiple instances of this class. +// This class can be used to emit single if-then-else block, or cascading +// else-if blocks. // // Usage: (check for the return value is omitted for simplicity) // // `if (cond) then_block` // IfThenElseEmitter ifThen(this); // emit(cond); -// ifThen.emitIf(); +// ifThen.emitThen(); // emit(then_block); // ifThen.emitEnd(); // // `if (cond) then_block else else_block` // IfThenElseEmitter ifThenElse(this); // emit(cond); -// ifThenElse.emitIfElse(); +// ifThenElse.emitThenElse(); // emit(then_block); // ifThenElse.emitElse(); // emit(else_block); // ifThenElse.emitEnd(); // +// `if (c1) b1 else if (c2) b2 else if (c3) b3 else b4` +// IfThenElseEmitter ifThenElse(this); +// emit(c1); +// ifThenElse.emitThenElse(); +// emit(b1); +// ifThenElse.emitElseIf(); +// emit(c2); +// ifThenElse.emitThenElse(); +// emit(b2); +// ifThenElse.emitElseIf(); +// emit(c3); +// ifThenElse.emitThenElse(); +// emit(b3); +// ifThenElse.emitElse(); +// emit(b4); +// ifThenElse.emitEnd(); +// // `cond ? then_expr : else_expr` // IfThenElseEmitter condElse(this); // emit(cond); // condElse.emitCond(); // emit(then_block); // condElse.emitElse(); // emit(else_block); // condElse.emitEnd(); // class MOZ_STACK_CLASS IfThenElseEmitter { BytecodeEmitter* bce_; + // Jump around the then clause, to the beginning of the else clause. JumpList jumpAroundThen_; + + // Jump around the else clause, to the end of the entire branch. JumpList jumpsAroundElse_; // The stack depth before emitting the then block. // Used for restoring stack depth before emitting the else block. // Also used for assertion to make sure then and else blocks pushed the // same number of values. int32_t thenDepth_; #ifdef DEBUG // The number of values pushed in the then and else blocks. int32_t pushed_; bool calculatedPushed_; -#endif // The state of this emitter. // - // +-------+ emitIf +----+ emitEnd +-----+ - // | Start |-+----------->| If |-------------------------+-------->| End | - // +-------+ | +----+ | +-----+ + // +-------+ emitCond +------+ emitElse +------+ emitEnd +-----+ + // | Start |-+--------->| Cond |--------->| Else |------>+------->| End | + // +-------+ | +------+ +------+ ^ +-----+ // | | - // | emitCond +------+ emitElse +------+ | - // +----------->| Cond |---+--------->| Else |-+ - // | +------+ | +------+ - // | | - // | emitIfElse +--------+ | - // +----------->| IfElse |-+ - // +--------+ - enum State { + // v emitThen +------+ | + // +->+--------->| Then |------------------------>+ + // ^ | +------+ ^ + // | | | + // | | +---+ + // | | | + // | | emitThenElse +----------+ emitElse +------+ | + // | +------------->| ThenElse |-+--------->| Else |-+ + // | +----------+ | +------+ + // | | + // | | emitElseIf +--------+ + // | +----------->| ElseIf |-+ + // | +--------+ | + // | | + // +------------------------------------------------------+ + enum class State { // The initial state. Start, - // After calling emitIf. - If, + // After calling emitThen. + Then, // After calling emitCond. Cond, - // After calling emitIfElse. - IfElse, - - // After calling Else. + // After calling emitThenElse. + ThenElse, + + // After calling emitElse. Else, + // After calling emitElseIf. + ElseIf, + // After calling emitEnd. End }; State state_; +#endif public: explicit IfThenElseEmitter(BytecodeEmitter* bce) : bce_(bce), - thenDepth_(0), + thenDepth_(0) #ifdef DEBUG - pushed_(0), - calculatedPushed_(false), + , pushed_(0) + , calculatedPushed_(false) + , state_(State::Start) #endif - state_(Start) {} ~IfThenElseEmitter() {} private: - bool emitIf(State nextState) { - MOZ_ASSERT(state_ == Start || state_ == Else); - MOZ_ASSERT(nextState == If || nextState == IfElse || nextState == Cond); - - // Clear jumpAroundThen_ offset that points previous JSOP_IFEQ. - if (state_ == Else) - jumpAroundThen_ = JumpList(); - + MOZ_MUST_USE bool emitIfInternal(SrcNoteType type) { // Emit an annotated branch-if-false around the then part. - SrcNoteType type = nextState == If ? SRC_IF : nextState == IfElse ? SRC_IF_ELSE : SRC_COND; if (!bce_->newSrcNote(type)) return false; if (!bce_->emitJump(JSOP_IFEQ, &jumpAroundThen_)) return false; // To restore stack depth in else part, save depth of the then part. #ifdef DEBUG // If DEBUG, this is also necessary to calculate |pushed_|. thenDepth_ = bce_->stackDepth; #else - if (nextState == IfElse || nextState == Cond) + if (type == SRC_COND || type == SRC_IF_ELSE) thenDepth_ = bce_->stackDepth; #endif - state_ = nextState; return true; } void calculateOrCheckPushed() { #ifdef DEBUG if (!calculatedPushed_) { pushed_ = bce_->stackDepth - thenDepth_; calculatedPushed_ = true; } else { MOZ_ASSERT(pushed_ == bce_->stackDepth - thenDepth_); } #endif } public: - bool emitIf() { - return emitIf(If); - } - - bool emitCond() { - return emitIf(Cond); - } - - bool emitIfElse() { - return emitIf(IfElse); - } - - bool emitElse() { - MOZ_ASSERT(state_ == IfElse || state_ == Cond); - + MOZ_MUST_USE bool emitThen() { + MOZ_ASSERT(state_ == State::Start || state_ == State::ElseIf); + if (!emitIfInternal(SRC_IF)) + return false; + +#ifdef DEBUG + state_ = State::Then; +#endif + return true; + } + + MOZ_MUST_USE bool emitCond() { + MOZ_ASSERT(state_ == State::Start); + if (!emitIfInternal(SRC_COND)) + return false; + +#ifdef DEBUG + state_ = State::Cond; +#endif + return true; + } + + MOZ_MUST_USE bool emitThenElse() { + MOZ_ASSERT(state_ == State::Start || state_ == State::ElseIf); + if (!emitIfInternal(SRC_IF_ELSE)) + return false; + +#ifdef DEBUG + state_ = State::ThenElse; +#endif + return true; + } + + private: + MOZ_MUST_USE bool emitElseInternal() { calculateOrCheckPushed(); // Emit a jump from the end of our then part around the else part. The // patchJumpsToTarget call at the bottom of this function will fix up // the offset with jumpsAroundElse value. if (!bce_->emitJump(JSOP_GOTO, &jumpsAroundElse_)) return false; // Ensure the branch-if-false comes here, then emit the else. if (!bce_->emitJumpTargetAndPatch(jumpAroundThen_)) return false; + // Clear jumpAroundThen_ offset, to tell emitEnd there was an else part. + jumpAroundThen_ = JumpList(); + // Restore stack depth of the then part. bce_->stackDepth = thenDepth_; - state_ = Else; +#ifdef DEBUG + state_ = State::Else; +#endif + return true; + } + + public: + MOZ_MUST_USE bool emitElse() { + MOZ_ASSERT(state_ == State::ThenElse || state_ == State::Cond); + + if (!emitElseInternal()) + return false; + +#ifdef DEBUG + state_ = State::Else; +#endif return true; } - bool emitEnd() { - MOZ_ASSERT(state_ == If || state_ == Else); + MOZ_MUST_USE bool emitElseIf() { + MOZ_ASSERT(state_ == State::ThenElse); + + if (!emitElseInternal()) + return false; + +#ifdef DEBUG + state_ = State::ElseIf; +#endif + return true; + } + + MOZ_MUST_USE bool emitEnd() { + MOZ_ASSERT(state_ == State::Then || state_ == State::Else); + // If there was an else part for the last branch, jumpAroundThen_ is + // already fixed up when emitting the else part. + MOZ_ASSERT_IF(state_ == State::Then, jumpAroundThen_.offset != -1); + MOZ_ASSERT_IF(state_ == State::Else, jumpAroundThen_.offset == -1); calculateOrCheckPushed(); - if (state_ == If) { - // No else part, fixup the branch-if-false to come here. + if (jumpAroundThen_.offset != -1) { + // No else part for the last branch, fixup the branch-if-false to + // come here. if (!bce_->emitJumpTargetAndPatch(jumpAroundThen_)) return false; } // Patch all the jumps around else parts. if (!bce_->emitJumpTargetAndPatch(jumpsAroundElse_)) return false; - state_ = End; +#ifdef DEBUG + state_ = State::End; +#endif return true; } #ifdef DEBUG // Returns the number of values pushed onto the value stack inside // `then_block` and `else_block`. // Can be used in assertion after emitting if-then-else. int32_t pushed() const { @@ -2181,18 +2269,17 @@ class ForOfLoopControl : public LoopCont iterDepth_(iterDepth), numYieldsAtBeginCodeNeedingIterClose_(UINT32_MAX), allowSelfHosted_(allowSelfHosted), iterKind_(iterKind) { } bool emitBeginCodeNeedingIteratorClose(BytecodeEmitter* bce) { - tryCatch_.emplace(bce, TryEmitter::TryCatch, TryEmitter::DontUseRetVal, - TryEmitter::DontUseControl); + tryCatch_.emplace(bce, TryEmitter::Kind::TryCatch, TryEmitter::ControlKind::NonSyntactic); if (!tryCatch_->emitTry()) return false; MOZ_ASSERT(numYieldsAtBeginCodeNeedingIterClose_ == UINT32_MAX); numYieldsAtBeginCodeNeedingIterClose_ = bce->yieldAndAwaitOffsetList.numYields; return true; @@ -2212,17 +2299,17 @@ class ForOfLoopControl : public LoopCont // IteratorClose for non-local jump, and we should't perform // IteratorClose again here. if (!bce->emit1(JSOP_UNDEFINED)) // ITER ... EXCEPTION ITER UNDEF return false; if (!bce->emit1(JSOP_STRICTNE)) // ITER ... EXCEPTION NE return false; IfThenElseEmitter ifIteratorIsNotClosed(bce); - if (!ifIteratorIsNotClosed.emitIf()) // ITER ... EXCEPTION + if (!ifIteratorIsNotClosed.emitThen()) // ITER ... EXCEPTION return false; MOZ_ASSERT(slotFromTop == unsigned(bce->stackDepth - iterDepth_)); if (!bce->emitDupAt(slotFromTop)) // ITER ... EXCEPTION ITER return false; if (!emitIteratorCloseInInnermostScope(bce, CompletionKind::Throw)) return false; // ITER ... EXCEPTION @@ -2238,17 +2325,17 @@ class ForOfLoopControl : public LoopCont uint32_t numYieldsEmitted = bce->yieldAndAwaitOffsetList.numYields; if (numYieldsEmitted > numYieldsAtBeginCodeNeedingIterClose_) { if (!tryCatch_->emitFinally()) return false; IfThenElseEmitter ifGeneratorClosing(bce); if (!bce->emit1(JSOP_ISGENCLOSING)) // ITER ... FTYPE FVALUE CLOSING return false; - if (!ifGeneratorClosing.emitIf()) // ITER ... FTYPE FVALUE + if (!ifGeneratorClosing.emitThen()) // ITER ... FTYPE FVALUE return false; if (!bce->emitDupAt(slotFromTop + 1)) // ITER ... FTYPE FVALUE ITER return false; if (!emitIteratorCloseInInnermostScope(bce, CompletionKind::Normal)) return false; // ITER ... FTYPE FVALUE if (!ifGeneratorClosing.emitEnd()) // ITER ... FTYPE FVALUE return false; } @@ -5418,17 +5505,17 @@ BytecodeEmitter::emitIteratorCloseInScop // Step 4. // // Do nothing if "return" is undefined or null. IfThenElseEmitter ifReturnMethodIsDefined(this); if (!emitPushNotUndefinedOrNull()) // ... ITER RET NOT-UNDEF-OR-NULL return false; - if (!ifReturnMethodIsDefined.emitIfElse()) // ... ITER RET + if (!ifReturnMethodIsDefined.emitThenElse()) // ... ITER RET return false; if (completionKind == CompletionKind::Throw) { // 7.4.6 IteratorClose ( iterator, completion ) // ... // 3. Let return be ? GetMethod(iterator, "return"). // 4. If return is undefined, return Completion(completion). // 5. Let innerResult be Call(return, iterator, « »). @@ -5454,18 +5541,17 @@ BytecodeEmitter::emitIteratorCloseInScop // Call "return" if it is not undefined or null, and check that it returns // an Object. if (!emit1(JSOP_SWAP)) // ... RET ITER return false; Maybe<TryEmitter> tryCatch; if (completionKind == CompletionKind::Throw) { - tryCatch.emplace(this, TryEmitter::TryCatch, TryEmitter::DontUseRetVal, - TryEmitter::DontUseControl); + tryCatch.emplace(this, TryEmitter::Kind::TryCatch, TryEmitter::ControlKind::NonSyntactic); // Mutate stack to balance stack for try-catch. if (!emit1(JSOP_UNDEFINED)) // ... RET ITER UNDEF return false; if (!tryCatch->emitTry()) // ... RET ITER UNDEF return false; if (!emitDupAt(2)) // ... RET ITER UNDEF RET return false; @@ -5799,17 +5885,17 @@ BytecodeEmitter::emitDestructuringOpsArr } if (member->isKind(ParseNodeKind::Spread)) { IfThenElseEmitter ifThenElse(this); if (!isFirst) { // If spread is not the first element of the pattern, // iterator can already be completed. // ... OBJ NEXT ITER *LREF DONE - if (!ifThenElse.emitIfElse()) // ... OBJ NEXT ITER *LREF + if (!ifThenElse.emitThenElse()) // ... OBJ NEXT ITER *LREF return false; if (!emitUint32Operand(JSOP_NEWARRAY, 0)) // ... OBJ NEXT ITER *LREF ARRAY return false; if (!ifThenElse.emitElse()) // ... OBJ NEXT ITER *LREF return false; } @@ -5854,17 +5940,17 @@ BytecodeEmitter::emitDestructuringOpsArr if (member->isKind(ParseNodeKind::Assign)) pndefault = member->pn_right; MOZ_ASSERT(!member->isKind(ParseNodeKind::Spread)); IfThenElseEmitter ifAlreadyDone(this); if (!isFirst) { // ... OBJ NEXT ITER *LREF DONE - if (!ifAlreadyDone.emitIfElse()) // ... OBJ NEXT ITER *LREF + if (!ifAlreadyDone.emitThenElse()) // ... OBJ NEXT ITER *LREF return false; if (!emit1(JSOP_UNDEFINED)) // ... OBJ NEXT ITER *LREF UNDEF return false; if (!emit1(JSOP_NOP_DESTRUCTURING)) // ... OBJ NEXT ITER *LREF UNDEF return false; // The iterator is done. Unpick a TRUE value for DONE above ITER. @@ -5889,17 +5975,17 @@ BytecodeEmitter::emitDestructuringOpsArr return false; if (!emit1(JSOP_DUP)) // ... OBJ NEXT ITER *LREF RESULT DONE DONE return false; if (!emit2(JSOP_UNPICK, emitted + 2)) // ... OBJ NEXT ITER DONE *LREF RESULT DONE return false; IfThenElseEmitter ifDone(this); - if (!ifDone.emitIfElse()) // ... OBJ NEXT ITER DONE *LREF RESULT + if (!ifDone.emitThenElse()) // ... OBJ NEXT ITER DONE *LREF RESULT return false; if (!emit1(JSOP_POP)) // ... OBJ NEXT ITER DONE *LREF return false; if (!emit1(JSOP_UNDEFINED)) // ... OBJ NEXT ITER DONE *LREF UNDEF return false; if (!emit1(JSOP_NOP_DESTRUCTURING)) // ... OBJ NEXT ITER DONE *LREF UNDEF return false; @@ -5941,17 +6027,17 @@ BytecodeEmitter::emitDestructuringOpsArr return false; } } // The last DONE value is on top of the stack. If not DONE, call // IteratorClose. // ... OBJ NEXT ITER DONE IfThenElseEmitter ifDone(this); - if (!ifDone.emitIfElse()) // ... OBJ NEXT ITER + if (!ifDone.emitThenElse()) // ... OBJ NEXT ITER return false; if (!emitPopN(2)) // ... OBJ return false; if (!ifDone.emitElse()) // ... OBJ NEXT ITER return false; if (!emit1(JSOP_SWAP)) // ... OBJ ITER NEXT return false; if (!emit1(JSOP_POP)) // ... OBJ ITER @@ -6783,24 +6869,24 @@ MOZ_NEVER_INLINE bool BytecodeEmitter::emitTry(ParseNode* pn) { ParseNode* catchScope = pn->pn_kid2; ParseNode* finallyNode = pn->pn_kid3; TryEmitter::Kind kind; if (catchScope) { if (finallyNode) - kind = TryEmitter::TryCatchFinally; + kind = TryEmitter::Kind::TryCatchFinally; else - kind = TryEmitter::TryCatch; + kind = TryEmitter::Kind::TryCatch; } else { MOZ_ASSERT(finallyNode); - kind = TryEmitter::TryFinally; - } - TryEmitter tryCatch(this, kind); + kind = TryEmitter::Kind::TryFinally; + } + TryEmitter tryCatch(this, kind, TryEmitter::ControlKind::Syntactic); if (!tryCatch.emitTry()) return false; if (!emitTree(pn->pn_kid1)) return false; // If this try has a catch block, emit it. @@ -6847,36 +6933,40 @@ BytecodeEmitter::emitIf(ParseNode* pn) if_again: /* Emit code for the condition before pushing stmtInfo. */ if (!emitTreeInBranch(pn->pn_kid1)) return false; ParseNode* elseNode = pn->pn_kid3; if (elseNode) { - if (!ifThenElse.emitIfElse()) + if (!ifThenElse.emitThenElse()) return false; } else { - if (!ifThenElse.emitIf()) + if (!ifThenElse.emitThen()) return false; } /* Emit code for the then part. */ if (!emitTreeInBranch(pn->pn_kid2)) return false; if (elseNode) { - if (!ifThenElse.emitElse()) - return false; - if (elseNode->isKind(ParseNodeKind::If)) { pn = elseNode; + + if (!ifThenElse.emitElseIf()) + return false; + goto if_again; } + if (!ifThenElse.emitElse()) + return false; + /* Emit code for the else part. */ if (!emitTreeInBranch(elseNode)) return false; } if (!ifThenElse.emitEnd()) return false; @@ -7069,17 +7159,17 @@ BytecodeEmitter::emitAsyncIterator() if (!emitElemOpBase(JSOP_CALLELEM)) // OBJ ITERFN return false; IfThenElseEmitter ifAsyncIterIsUndefined(this); if (!emitPushNotUndefinedOrNull()) // OBJ ITERFN !UNDEF-OR-NULL return false; if (!emit1(JSOP_NOT)) // OBJ ITERFN UNDEF-OR-NULL return false; - if (!ifAsyncIterIsUndefined.emitIfElse()) // OBJ ITERFN + if (!ifAsyncIterIsUndefined.emitThenElse()) // OBJ ITERFN return false; if (!emit1(JSOP_POP)) // OBJ return false; if (!emit1(JSOP_DUP)) // OBJ OBJ return false; if (!emit2(JSOP_SYMBOL, uint8_t(JS::SymbolCode::iterator))) // OBJ OBJ @@ITERATOR return false; @@ -7384,17 +7474,17 @@ BytecodeEmitter::emitForOf(ParseNode* fo if (!emit1(JSOP_DUP)) // NEXT ITER RESULT RESULT return false; if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // NEXT ITER RESULT DONE return false; IfThenElseEmitter ifDone(this); - if (!ifDone.emitIf()) // NEXT ITER RESULT + if (!ifDone.emitThen()) // NEXT ITER RESULT return false; // Remove RESULT from the stack to release it. if (!emit1(JSOP_POP)) // NEXT ITER return false; if (!emit1(JSOP_UNDEFINED)) // NEXT ITER UNDEF return false; @@ -8532,18 +8622,18 @@ BytecodeEmitter::emitYieldStar(ParseNode // Initial send value is undefined. if (!emit1(JSOP_UNDEFINED)) // NEXT ITER RECEIVED return false; int32_t savedDepthTemp; int32_t startDepth = stackDepth; MOZ_ASSERT(startDepth >= 3); - TryEmitter tryCatch(this, TryEmitter::TryCatchFinally, TryEmitter::DontUseRetVal, - TryEmitter::DontUseControl); + TryEmitter tryCatch(this, TryEmitter::Kind::TryCatchFinally, + TryEmitter::ControlKind::NonSyntactic); if (!tryCatch.emitJumpOverCatchAndFinally()) // NEXT ITER RESULT return false; JumpTarget tryStart{ offset() }; if (!tryCatch.emitTry()) // NEXT ITER RESULT return false; MOZ_ASSERT(this->stackDepth == startDepth); @@ -8577,17 +8667,17 @@ BytecodeEmitter::emitYieldStar(ParseNode if (!emit1(JSOP_DUP)) // NEXT ITER RESULT EXCEPTION ITER THROW THROW return false; if (!emit1(JSOP_UNDEFINED)) // NEXT ITER RESULT EXCEPTION ITER THROW THROW UNDEFINED return false; if (!emit1(JSOP_EQ)) // NEXT ITER RESULT EXCEPTION ITER THROW ?EQL return false; IfThenElseEmitter ifThrowMethodIsNotDefined(this); - if (!ifThrowMethodIsNotDefined.emitIf()) // NEXT ITER RESULT EXCEPTION ITER THROW + if (!ifThrowMethodIsNotDefined.emitThen()) // NEXT ITER RESULT EXCEPTION ITER THROW return false; savedDepthTemp = stackDepth; if (!emit1(JSOP_POP)) // NEXT ITER RESULT EXCEPTION ITER return false; // ES 14.4.13, YieldExpression : yield * AssignmentExpression, step 5.b.iii.2 // // If the iterator does not have a "throw" method, it calls IteratorClose // and then throws a TypeError. @@ -8635,17 +8725,17 @@ BytecodeEmitter::emitYieldStar(ParseNode // ES 14.4.13, yield * AssignmentExpression, step 5.c // // Call iterator.return() for receiving a "forced return" completion from // the generator. IfThenElseEmitter ifGeneratorClosing(this); if (!emit1(JSOP_ISGENCLOSING)) // NEXT ITER RESULT FTYPE FVALUE CLOSING return false; - if (!ifGeneratorClosing.emitIf()) // NEXT ITER RESULT FTYPE FVALUE + if (!ifGeneratorClosing.emitThen()) // NEXT ITER RESULT FTYPE FVALUE return false; // Step ii. // // Get the "return" method. if (!emitDupAt(3)) // NEXT ITER RESULT FTYPE FVALUE ITER return false; if (!emit1(JSOP_DUP)) // NEXT ITER RESULT FTYPE FVALUE ITER ITER @@ -8659,17 +8749,17 @@ BytecodeEmitter::emitYieldStar(ParseNode IfThenElseEmitter ifReturnMethodIsDefined(this); if (!emitPushNotUndefinedOrNull()) // NEXT ITER RESULT FTYPE FVALUE ITER RET NOT-UNDEF-OR-NULL return false; // Step iv. // // Call "return" with the argument passed to Generator.prototype.return, // which is currently in rval.value. - if (!ifReturnMethodIsDefined.emitIfElse()) // NEXT ITER OLDRESULT FTYPE FVALUE ITER RET + if (!ifReturnMethodIsDefined.emitThenElse()) // NEXT ITER OLDRESULT FTYPE FVALUE ITER RET return false; if (!emit1(JSOP_SWAP)) // NEXT ITER OLDRESULT FTYPE FVALUE RET ITER return false; if (!emit1(JSOP_GETRVAL)) // NEXT ITER OLDRESULT FTYPE FVALUE RET ITER RVAL return false; if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // NEXT ITER OLDRESULT FTYPE FVALUE RET ITER VALUE return false; if (!emitCall(JSOP_CALL, 1)) // NEXT ITER OLDRESULT FTYPE FVALUE RESULT @@ -8689,17 +8779,17 @@ BytecodeEmitter::emitYieldStar(ParseNode // // Check if the returned object from iterator.return() is done. If not, // continuing yielding. IfThenElseEmitter ifReturnDone(this); if (!emit1(JSOP_DUP)) // NEXT ITER OLDRESULT FTYPE FVALUE RESULT RESULT return false; if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // NEXT ITER OLDRESULT FTYPE FVALUE RESULT DONE return false; - if (!ifReturnDone.emitIfElse()) // NEXT ITER OLDRESULT FTYPE FVALUE RESULT + if (!ifReturnDone.emitThenElse()) // NEXT ITER OLDRESULT FTYPE FVALUE RESULT return false; if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // NEXT ITER OLDRESULT FTYPE FVALUE VALUE return false; if (!emitPrepareIteratorResult()) // NEXT ITER OLDRESULT FTYPE FVALUE VALUE RESULT return false; if (!emit1(JSOP_SWAP)) // NEXT ITER OLDRESULT FTYPE FVALUE RESULT VALUE return false; @@ -9448,17 +9538,17 @@ BytecodeEmitter::emitCallOrNew(ParseNode return false; if (!emit1(JSOP_OPTIMIZE_SPREADCALL)) return false; if (!emit1(JSOP_NOT)) return false; - if (!ifNotOptimizable.emitIf()) + if (!ifNotOptimizable.emitThen()) return false; if (!emit1(JSOP_POP)) return false; } if (!emitArray(args, argc)) return false; @@ -10576,17 +10666,17 @@ BytecodeEmitter::emitClass(ParseNode* pn if (!emit1(JSOP_DUP)) // ... HERITAGE HERITAGE return false; if (!emit1(JSOP_NULL)) // ... HERITAGE HERITAGE NULL return false; if (!emit1(JSOP_STRICTNE)) // ... HERITAGE NE return false; // [THEN] funProto = heritage, objProto = heritage.prototype - if (!ifThenElse.emitIfElse()) + if (!ifThenElse.emitThenElse()) return false; if (!emit1(JSOP_DUP)) // ... HERITAGE HERITAGE return false; if (!emitAtomOp(cx->names().prototype, JSOP_GETPROP)) // ... HERITAGE PROTO return false; // [ELSE] funProto = %FunctionPrototype%, objProto = null if (!ifThenElse.emitElse())
--- a/js/src/gc/Allocator.cpp +++ b/js/src/gc/Allocator.cpp @@ -81,17 +81,17 @@ template JSObject* js::Allocate<JSObject template <AllowGC allowGC> JSObject* GCRuntime::tryNewNurseryObject(JSContext* cx, size_t thingSize, size_t nDynamicSlots, const Class* clasp) { MOZ_RELEASE_ASSERT(!cx->helperThread()); MOZ_ASSERT(cx->isNurseryAllocAllowed()); MOZ_ASSERT(!cx->isNurseryAllocSuppressed()); - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); JSObject* obj = cx->nursery().allocateObject(cx, thingSize, nDynamicSlots, clasp); if (obj) return obj; if (allowGC && !cx->suppressGC) { cx->runtime()->gc.minorGC(JS::gcreason::OUT_OF_NURSERY); @@ -135,17 +135,17 @@ GCRuntime::tryNewTenuredObject(JSContext template <AllowGC allowGC> JSString* GCRuntime::tryNewNurseryString(JSContext* cx, size_t thingSize, AllocKind kind) { MOZ_ASSERT(IsNurseryAllocable(kind)); MOZ_ASSERT(cx->isNurseryAllocAllowed()); MOZ_ASSERT(!cx->helperThread()); MOZ_ASSERT(!cx->isNurseryAllocSuppressed()); - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); Cell* cell = cx->nursery().allocateString(cx->zone(), thingSize, kind); if (cell) return static_cast<JSString*>(cell); if (allowGC && !cx->suppressGC) { cx->runtime()->gc.minorGC(JS::gcreason::OUT_OF_NURSERY); @@ -270,23 +270,23 @@ bool GCRuntime::checkAllocatorState(JSContext* cx, AllocKind kind) { if (allowGC) { if (!gcIfNeededAtAllocation(cx)) return false; } #if defined(JS_GC_ZEAL) || defined(DEBUG) - MOZ_ASSERT_IF(cx->realm()->isAtomsRealm(), + MOZ_ASSERT_IF(cx->zone()->isAtomsZone(), kind == AllocKind::ATOM || kind == AllocKind::FAT_INLINE_ATOM || kind == AllocKind::SYMBOL || kind == AllocKind::JITCODE || kind == AllocKind::SCOPE); - MOZ_ASSERT_IF(!cx->realm()->isAtomsRealm(), + MOZ_ASSERT_IF(!cx->zone()->isAtomsZone(), kind != AllocKind::ATOM && kind != AllocKind::FAT_INLINE_ATOM); MOZ_ASSERT(!JS::CurrentThreadIsHeapBusy()); MOZ_ASSERT(cx->isAllocAllowed()); #endif // Crash if we perform a GC action when it is not safe. if (allowGC && !cx->suppressGC)
--- a/js/src/gc/AtomMarking.cpp +++ b/js/src/gc/AtomMarking.cpp @@ -78,17 +78,17 @@ bool AtomMarkingRuntime::computeBitmapFromChunkMarkBits(JSRuntime* runtime, DenseBitmap& bitmap) { MOZ_ASSERT(CurrentThreadIsPerformingGC()); MOZ_ASSERT(!runtime->hasHelperThreadZones()); if (!bitmap.ensureSpace(allocatedWords)) return false; - Zone* atomsZone = runtime->unsafeAtomsRealm()->zone(); + Zone* atomsZone = runtime->unsafeAtomsZone(); for (auto thingKind : AllAllocKinds()) { for (ArenaIter aiter(atomsZone, thingKind); !aiter.done(); aiter.next()) { Arena* arena = aiter.get(); uintptr_t* chunkWords = arena->chunk()->bitmap.arenaBits(arena); bitmap.copyBitsFrom(arena->atomBitmapStart(), ArenaBitmapWords, chunkWords); } } @@ -112,17 +112,17 @@ template <typename Bitmap> static void AddBitmapToChunkMarkBits(JSRuntime* runtime, Bitmap& bitmap) { // Make sure that by copying the mark bits for one arena in word sizes we // do not affect the mark bits for other arenas. static_assert(ArenaBitmapBits == ArenaBitmapWords * JS_BITS_PER_WORD, "ArenaBitmapWords must evenly divide ArenaBitmapBits"); - Zone* atomsZone = runtime->unsafeAtomsRealm()->zone(); + Zone* atomsZone = runtime->unsafeAtomsZone(); for (auto thingKind : AllAllocKinds()) { for (ArenaIter aiter(atomsZone, thingKind); !aiter.done(); aiter.next()) { Arena* arena = aiter.get(); uintptr_t* chunkWords = arena->chunk()->bitmap.arenaBits(arena); bitmap.bitwiseOrRangeInto(arena->atomBitmapStart(), ArenaBitmapWords, chunkWords); } } }
--- a/js/src/gc/GC.cpp +++ b/js/src/gc/GC.cpp @@ -3847,17 +3847,16 @@ Zone::sweepCompartments(FreeOp* fop, boo JSCompartment** read = compartments().begin(); JSCompartment** end = compartments().end(); JSCompartment** write = read; bool foundOne = false; while (read < end) { JSCompartment* comp = *read++; Realm* realm = JS::GetRealmForCompartment(comp); - MOZ_ASSERT(!realm->isAtomsRealm()); /* * Don't delete the last compartment and realm if all the ones before * it were deleted and keepAtleastOne is true. */ bool dontDelete = read == end && !foundOne && keepAtleastOne; if ((!realm->marked() && !dontDelete) || destroyingRuntime) { realm->destroy(fop); @@ -4121,17 +4120,17 @@ struct MaybeCompartmentFunctor { template <typename T> JSCompartment* operator()(T* t) { return t->maybeCompartment(); } }; void CompartmentCheckTracer::onChild(const JS::GCCellPtr& thing) { JSCompartment* comp = DispatchTyped(MaybeCompartmentFunctor(), thing); if (comp && compartment) { - MOZ_ASSERT(comp == compartment || runtime()->isAtomsCompartment(comp) || + MOZ_ASSERT(comp == compartment || (srcKind == JS::TraceKind::Object && InCrossCompartmentMap(static_cast<JSObject*>(src), thing))); } else { TenuredCell* tenured = TenuredCell::fromPointer(thing.asCell()); Zone* thingZone = tenured->zoneFromAnyThread(); MOZ_ASSERT(thingZone == zone || thingZone->isAtomsZone()); } } @@ -4218,17 +4217,17 @@ ShouldCollectZone(Zone* zone, JS::gcreas bool GCRuntime::prepareZonesForCollection(JS::gcreason::Reason reason, bool* isFullOut, AutoLockForExclusiveAccess& lock) { #ifdef DEBUG /* Assert that zone state is as we expect */ for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { MOZ_ASSERT(!zone->isCollecting()); - MOZ_ASSERT(!zone->compartments().empty()); + MOZ_ASSERT_IF(!zone->isAtomsZone(), !zone->compartments().empty()); for (auto i : AllAllocKinds()) MOZ_ASSERT(!zone->arenas.arenaListsToSweep(i)); } #endif *isFullOut = true; bool any = false; @@ -4246,17 +4245,17 @@ GCRuntime::prepareZonesForCollection(JS: zone->setPreservingCode(false); } // Discard JIT code more aggressively if the process is approaching its // executable code limit. bool canAllocateMoreCode = jit::CanLikelyAllocateMoreExecutableMemory(); - for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) { + for (CompartmentsIter c(rt); !c.done(); c.next()) { c->scheduledForDestruction = false; c->maybeAlive = false; for (RealmsInCompartmentIter r(c); !r.done(); r.next()) { r->unmark(); if (r->shouldTraceGlobal() || !r->zone()->isGCScheduled()) c->maybeAlive = true; if (shouldPreserveJITCode(r, currentTime, reason, canAllocateMoreCode)) r->zone()->setPreservingCode(true); @@ -4473,17 +4472,17 @@ GCRuntime::markCompartments() * for details on this problem. To avoid the problem, we try to avoid * allocation and read barriers during JS_TransplantObject and the like. */ /* Propagate the maybeAlive flag via cross-compartment edges. */ Vector<JSCompartment*, 0, js::SystemAllocPolicy> workList; - for (CompartmentsIter comp(rt, SkipAtoms); !comp.done(); comp.next()) { + for (CompartmentsIter comp(rt); !comp.done(); comp.next()) { if (comp->maybeAlive) { if (!workList.append(comp)) return; } } while (!workList.empty()) { JSCompartment* comp = workList.popCopy(); @@ -4890,17 +4889,17 @@ GCRuntime::finishMarkingValidation() static void DropStringWrappers(JSRuntime* rt) { /* * String "wrappers" are dropped on GC because their presence would require * us to sweep the wrappers in all compartments every time we sweep a * compartment group. */ - for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { + for (CompartmentsIter c(rt); !c.done(); c.next()) { for (JSCompartment::StringWrapperEnum e(c); !e.empty(); e.popFront()) { MOZ_ASSERT(e.front().key().is<JSString*>()); e.removeFront(); } } } /* @@ -5132,17 +5131,17 @@ AssertNotOnGrayList(JSObject* obj) GetProxyReservedSlot(obj, ProxyObject::grayLinkReservedSlot(obj)).isUndefined()); } #endif static void AssertNoWrappersInGrayList(JSRuntime* rt) { #ifdef DEBUG - for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { + for (CompartmentsIter c(rt); !c.done(); c.next()) { MOZ_ASSERT(!c->gcIncomingGrayPointers); for (JSCompartment::NonStringWrapperEnum e(c); !e.empty(); e.popFront()) AssertNotOnGrayList(&e.front().value().unbarrieredGet().toObject()); } #endif } static JSObject* @@ -5407,17 +5406,17 @@ UpdateAtomsBitmap(GCParallelTask* task) // conservative to just not call it. } runtime->gc.atomMarking.updateChunkMarkBits(runtime); // For convenience sweep these tables non-incrementally as part of bitmap // sweeping; they are likely to be much smaller than the main atoms table. runtime->unsafeSymbolRegistry().sweep(); - for (RealmsIter realm(runtime, SkipAtoms); !realm.done(); realm.next()) + for (RealmsIter realm(runtime); !realm.done(); realm.next()) realm->sweepVarNames(); } static void SweepCCWrappers(GCParallelTask* task) { JSRuntime* runtime = task->runtime(); for (SweepGroupCompartmentsIter c(runtime); !c.done(); c.next()) @@ -6883,17 +6882,17 @@ GCRuntime::resetIncrementalGC(gc::AbortR MOZ_ASSERT(!marker.shouldCheckCompartments()); break; } case State::Sweep: { marker.reset(); - for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) + for (CompartmentsIter c(rt); !c.done(); c.next()) c->scheduledForDestruction = false; /* Finish sweeping the current sweep group, then abort. */ abortSweepAfterCurrentGroup = true; /* Don't perform any compaction after sweeping. */ bool wasCompacting = isCompacting; isCompacting = false; @@ -7547,17 +7546,17 @@ GCRuntime::scanZonesBeforeGC() void GCRuntime::maybeDoCycleCollection() { const static double ExcessiveGrayRealms = 0.8; const static size_t LimitGrayRealms = 200; size_t realmsTotal = 0; size_t realmsGray = 0; - for (RealmsIter realm(rt, SkipAtoms); !realm.done(); realm.next()) { + for (RealmsIter realm(rt); !realm.done(); realm.next()) { ++realmsTotal; GlobalObject* global = realm->unsafeUnbarrieredMaybeGlobal(); if (global && global->isMarkedGray()) ++realmsGray; } double grayFraction = double(realmsGray) / double(realmsTotal); if (grayFraction > ExcessiveGrayRealms || realmsGray > LimitGrayRealms) callDoCycleCollectionCallback(rt->mainContextFromOwnThread()); @@ -7597,17 +7596,17 @@ bool GCRuntime::shouldRepeatForDeadZone(JS::gcreason::Reason reason) { MOZ_ASSERT_IF(reason == JS::gcreason::COMPARTMENT_REVIVED, !isIncremental); MOZ_ASSERT(!isIncrementalGCInProgress()); if (!isIncremental) return false; - for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { + for (CompartmentsIter c(rt); !c.done(); c.next()) { if (c->scheduledForDestruction) return true; } return false; } void @@ -7956,17 +7955,17 @@ js::NewRealm(JSContext* cx, JSPrincipals } } ScopedJSDeletePtr<Realm> realm(cx->new_<Realm>(zone, options)); if (!realm || !realm->init(cx)) return nullptr; // Set up the principals. - JS_SetCompartmentPrincipals(JS::GetCompartmentForRealm(realm), principals); + JS::SetRealmPrincipals(realm, principals); JSCompartment* comp = realm->compartment(); if (!comp->realms().append(realm)) { ReportOutOfMemory(cx); return nullptr; } AutoLockGC lock(rt); @@ -8483,17 +8482,17 @@ js::gc::CheckHashTablesAfterMovingGC(JSR JS::AutoCheckCannotGC nogc; for (auto baseShape = zone->cellIter<BaseShape>(); !baseShape.done(); baseShape.next()) { if (ShapeTable* table = baseShape->maybeTable(nogc)) table->checkAfterMovingGC(); } } - for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { + for (CompartmentsIter c(rt); !c.done(); c.next()) { c->checkWrapperMapAfterMovingGC(); for (RealmsInCompartmentIter r(c); !r.done(); r.next()) { r->checkObjectGroupTablesAfterMovingGC(); r->dtoaCache.checkCacheAfterMovingGC(); r->checkScriptMapsAfterMovingGC(); if (r->debugEnvs()) r->debugEnvs()->checkHashTablesAfterMovingGC();
--- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -1024,17 +1024,17 @@ js::Nursery::sweep(JSTracer* trc) obj->zone()->removeUniqueId(obj); } else { JSObject* dst = Forwarded(obj); dst->zone()->transferUniqueId(dst, obj); } } cellsWithUid_.clear(); - for (CompartmentsIter c(runtime(), SkipAtoms); !c.done(); c.next()) + for (CompartmentsIter c(runtime()); !c.done(); c.next()) c->sweepAfterMinorGC(trc); sweepDictionaryModeObjects(); sweepMapAndSetObjects(); } void js::Nursery::clear()
--- a/js/src/gc/PrivateIterators-inl.h +++ b/js/src/gc/PrivateIterators-inl.h @@ -62,17 +62,17 @@ class GCZonesIter ZonesIter zone; public: explicit GCZonesIter(JSRuntime* rt, ZoneSelector selector = WithAtoms) : zone(rt, selector) { MOZ_ASSERT(JS::CurrentThreadIsHeapBusy()); MOZ_ASSERT_IF(rt->gc.atomsZone->isCollectingFromAnyThread(), !rt->hasHelperThreadZones()); - if (!zone->isCollectingFromAnyThread()) + if (!done() && !zone->isCollectingFromAnyThread()) next(); } bool done() const { return zone.done(); } void next() { MOZ_ASSERT(!done()); do { @@ -90,28 +90,40 @@ class GCZonesIter }; using GCCompartmentsIter = CompartmentsOrRealmsIterT<GCZonesIter, CompartmentsInZoneIter>; using GCRealmsIter = CompartmentsOrRealmsIterT<GCZonesIter, RealmsInZoneIter>; /* Iterates over all zones in the current sweep group. */ class SweepGroupZonesIter { JS::Zone* current; + ZoneSelector selector; public: - explicit SweepGroupZonesIter(JSRuntime* rt) { + explicit SweepGroupZonesIter(JSRuntime* rt, ZoneSelector selector = WithAtoms) + : selector(selector) + { MOZ_ASSERT(CurrentThreadIsPerformingGC()); current = rt->gc.getCurrentSweepGroup(); + maybeSkipAtomsZone(); + } + + void maybeSkipAtomsZone() { + if (selector == SkipAtoms && current && current->isAtomsZone()) { + current = current->nextNodeInGroup(); + MOZ_ASSERT_IF(current, !current->isAtomsZone()); + } } bool done() const { return !current; } void next() { MOZ_ASSERT(!done()); current = current->nextNodeInGroup(); + maybeSkipAtomsZone(); } JS::Zone* get() const { MOZ_ASSERT(!done()); return current; } operator JS::Zone*() const { return get(); }
--- a/js/src/gc/PublicIterators.cpp +++ b/js/src/gc/PublicIterators.cpp @@ -136,23 +136,23 @@ js::IterateGrayObjectsUnderCC(Zone* zone } JS_PUBLIC_API(void) JS_IterateCompartments(JSContext* cx, void* data, JSIterateCompartmentCallback compartmentCallback) { AutoTraceSession session(cx->runtime()); - for (CompartmentsIter c(cx->runtime(), WithAtoms); !c.done(); c.next()) + for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) (*compartmentCallback)(cx, data, c); } JS_PUBLIC_API(void) JS::IterateRealms(JSContext* cx, void* data, JS::IterateRealmCallback realmCallback) { AutoTraceSession session(cx->runtime()); Rooted<Realm*> realm(cx); - for (RealmsIter r(cx->runtime(), WithAtoms); !r.done(); r.next()) { + for (RealmsIter r(cx->runtime()); !r.done(); r.next()) { realm = r; (*realmCallback)(cx, data, realm); } }
--- a/js/src/gc/PublicIterators.h +++ b/js/src/gc/PublicIterators.h @@ -188,24 +188,17 @@ class CompartmentsOrRealmsIterT using T = typename InnerIterT::ItemType; gc::AutoEnterIteration iterMarker; ZonesIterT zone; mozilla::Maybe<InnerIterT> inner; public: explicit CompartmentsOrRealmsIterT(JSRuntime* rt) - : iterMarker(&rt->gc), zone(rt) - { - if (!zone.done()) - inner.emplace(zone); - } - - CompartmentsOrRealmsIterT(JSRuntime* rt, ZoneSelector selector) - : iterMarker(&rt->gc), zone(rt, selector) + : iterMarker(&rt->gc), zone(rt, SkipAtoms) { if (!zone.done()) inner.emplace(zone); } bool done() const { return zone.done(); } void next() {
--- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -364,17 +364,17 @@ js::gc::GCRuntime::traceRuntimeCommon(JS // Trace the shared Intl data. rt->traceSharedIntlData(trc); // Trace the JSContext. rt->mainContextFromOwnThread()->trace(trc); // Trace all realm roots, but not the realm itself; it is traced via the // parent pointer if traceRoots actually traces anything. - for (RealmsIter r(rt, SkipAtoms); !r.done(); r.next()) + for (RealmsIter r(rt); !r.done(); r.next()) r->traceRoots(trc, traceOrMark); // Trace helper thread roots. HelperThreadState().trace(trc, session); // Trace the embedding's black and gray roots. if (!JS::CurrentThreadIsHeapMinorCollecting()) { gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::MARK_EMBEDDING); @@ -422,17 +422,17 @@ js::gc::GCRuntime::finishRoots() if (rootsHash.ref().initialized()) rootsHash.ref().clear(); rt->finishPersistentRoots(); rt->finishSelfHosting(); - for (RealmsIter r(rt, SkipAtoms); !r.done(); r.next()) + for (RealmsIter r(rt); !r.done(); r.next()) r->finishRoots(); #ifdef DEBUG // The nsWrapperCache may not be empty before our shutdown GC, so we have // to skip that table when verifying that we are fully unrooted. auto prior = grayRootTracer; grayRootTracer = Callback<JSTraceDataOp>(nullptr, nullptr);
--- a/js/src/gc/Tracer.cpp +++ b/js/src/gc/Tracer.cpp @@ -158,17 +158,17 @@ struct TraceIncomingFunctor { // reference. void operator()(JSString** tp) {} }; } // namespace (anonymous) JS_PUBLIC_API(void) JS::TraceIncomingCCWs(JSTracer* trc, const JS::CompartmentSet& compartments) { - for (js::CompartmentsIter comp(trc->runtime(), SkipAtoms); !comp.done(); comp.next()) { + for (js::CompartmentsIter comp(trc->runtime()); !comp.done(); comp.next()) { if (compartments.has(comp)) continue; for (JSCompartment::WrapperEnum e(comp); !e.empty(); e.popFront()) { mozilla::DebugOnly<const CrossCompartmentKey> prior = e.front().key(); e.front().mutableKey().applyToWrapped(TraceIncomingFunctor(trc, compartments)); MOZ_ASSERT(e.front().key() == prior); }
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/gc/bug-1464872.js @@ -0,0 +1,14 @@ +var g = newGlobal(); +var dbg = Debugger(g); +dbg.onEnterFrame = function(frame) {}; + +var g2 = newGlobal(); +g2[g] = g; +g2.evaluate("grayRoot()") +g2 = undefined; + +g = undefined; +dbg = undefined; + +gc(); +startgc(100000);
--- a/js/src/jit/BaselineDebugModeOSR.cpp +++ b/js/src/jit/BaselineDebugModeOSR.cpp @@ -1038,17 +1038,17 @@ BaselineFrame::deleteDebugModeOSRInfo() flags_ &= ~HAS_DEBUG_MODE_OSR_INFO; } JitCode* JitRuntime::getBaselineDebugModeOSRHandler(JSContext* cx) { if (!baselineDebugModeOSRHandler_) { AutoLockForExclusiveAccess lock(cx); - AutoAtomsRealm ar(cx, lock); + AutoAtomsZone az(cx, lock); uint32_t offset; if (JitCode* code = generateBaselineDebugModeOSRHandler(cx, &offset)) { baselineDebugModeOSRHandler_ = code; baselineDebugModeOSRHandlerNoFrameRegPopAddr_ = code->raw() + offset; } } return baselineDebugModeOSRHandler_;
--- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -103,27 +103,29 @@ jit::MaybeGetJitContext() return CurrentJitContext(); } JitContext::JitContext(CompileRuntime* rt, CompileRealm* realm, TempAllocator* temp) : cx(nullptr), temp(temp), runtime(rt), realm(realm), + zone(realm ? realm->zone() : nullptr), prev_(CurrentJitContext()), assemblerCount_(0) { SetJitContext(this); } JitContext::JitContext(JSContext* cx, TempAllocator* temp) : cx(cx), temp(temp), runtime(CompileRuntime::get(cx->runtime())), realm(CompileRealm::get(cx->realm())), + zone(CompileZone::get(cx->zone())), prev_(CurrentJitContext()), assemblerCount_(0) { SetJitContext(this); } JitContext::JitContext(TempAllocator* temp) : JitContext(nullptr, nullptr, temp) @@ -206,23 +208,20 @@ JitRuntime::startTrampolineCode(MacroAss masm.haltingAlign(CodeAlignment); masm.setFramePushed(0); return masm.currentOffset(); } bool JitRuntime::initialize(JSContext* cx, AutoLockForExclusiveAccess& lock) { - AutoAtomsRealm ar(cx, lock); + AutoAtomsZone az(cx, lock); JitContext jctx(cx, nullptr); - if (!cx->realm()->ensureJitRealmExists(cx)) - return false; - functionWrappers_ = cx->new_<VMWrapperMap>(cx); if (!functionWrappers_ || !functionWrappers_->init()) return false; StackMacroAssembler masm; Label bailoutTail; JitSpew(JitSpew_Codegen, "# Emitting bailout tail stub"); @@ -334,17 +333,17 @@ JitRuntime::initialize(JSContext* cx, Au JitCode* JitRuntime::debugTrapHandler(JSContext* cx) { if (!debugTrapHandler_) { // JitRuntime code stubs are shared across compartments and have to // be allocated in the atoms zone. AutoLockForExclusiveAccess lock(cx); - AutoAtomsRealm ar(cx, lock); + AutoAtomsZone az(cx, lock); debugTrapHandler_ = generateDebugTrapHandler(cx); } return debugTrapHandler_; } JitRuntime::IonBuilderList& JitRuntime::ionLazyLinkList(JSRuntime* rt) { @@ -587,17 +586,17 @@ JitRuntime::Trace(JSTracer* trc, AutoLoc { MOZ_ASSERT(!JS::CurrentThreadIsHeapMinorCollecting()); // Shared stubs are allocated in the atoms zone, so do not iterate // them after the atoms heap after it has been "finished." if (trc->runtime()->atomsAreFinished()) return; - Zone* zone = trc->runtime()->atomsRealm(lock)->zone(); + Zone* zone = trc->runtime()->atomsZone(lock); for (auto i = zone->cellIter<JitCode>(); !i.done(); i.next()) { JitCode* code = i; TraceRoot(trc, &code, "wrapper"); } } /* static */ void JitRuntime::TraceJitcodeGlobalTableForMinorGC(JSTracer* trc)
--- a/js/src/jit/Ion.h +++ b/js/src/jit/Ion.h @@ -67,16 +67,17 @@ class JitContext // Allocator for temporary memory during compilation. TempAllocator* temp; // Wrappers with information about the current runtime/realm for use // during compilation. CompileRuntime* runtime; CompileRealm* realm; + CompileZone* zone; int getNextAssemblerId() { return assemblerCount_++; } private: JitContext* prev_; int assemblerCount_; };
--- a/js/src/jit/JSJitFrameIter.h +++ b/js/src/jit/JSJitFrameIter.h @@ -341,19 +341,17 @@ class RInstructionResults RInstructionResults(RInstructionResults&& src); RInstructionResults& operator=(RInstructionResults&& rhs); ~RInstructionResults(); MOZ_MUST_USE bool init(JSContext* cx, uint32_t numResults); bool isInitialized() const; -#ifdef DEBUG size_t length() const; -#endif JitFrameLayout* frame() const; HeapPtr<Value>& operator[](size_t index); void trace(JSTracer* trc); };
--- a/js/src/jit/JitFrames.cpp +++ b/js/src/jit/JitFrames.cpp @@ -1489,23 +1489,21 @@ RInstructionResults::init(JSContext* cx, } bool RInstructionResults::isInitialized() const { return initialized_; } -#ifdef DEBUG size_t RInstructionResults::length() const { return results_->length(); } -#endif JitFrameLayout* RInstructionResults::frame() const { MOZ_ASSERT(fp_); return fp_; } @@ -1948,17 +1946,17 @@ SnapshotIterator::initInstructionResults // If the evaluation failed because of OOMs, then we discard the // current set of result that we collected so far. fallback.activation->removeIonFrameRecovery(fp); return false; } } MOZ_ASSERT(results->isInitialized()); - MOZ_ASSERT(results->length() == recover_.numInstructions() - 1); + MOZ_RELEASE_ASSERT(results->length() == recover_.numInstructions() - 1); instructionResults_ = results; return true; } bool SnapshotIterator::computeInstructionResults(JSContext* cx, RInstructionResults* results) const { MOZ_ASSERT(!results->isInitialized());
--- a/js/src/jit/shared/Assembler-shared.h +++ b/js/src/jit/shared/Assembler-shared.h @@ -152,18 +152,18 @@ struct Imm64 inline Imm32 firstHalf() const; inline Imm32 secondHalf() const; }; #ifdef DEBUG static inline bool IsCompilingWasm() { - // wasm compilation pushes a JitContext with a null Realm. - return GetJitContext()->realm == nullptr; + // wasm compilation pushes a JitContext with a null Zone. + return GetJitContext()->zone == nullptr; } #endif // Pointer to be embedded as an immediate in an instruction. struct ImmPtr { void* value;
--- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -792,17 +792,17 @@ JS_WrapValue(JSContext* cx, MutableHandl return cx->compartment()->wrap(cx, vp); } static void ReleaseAssertObjectHasNoWrappers(JSContext* cx, HandleObject target) { RootedValue origv(cx, ObjectValue(*target)); - for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) { + for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) { if (c->lookupWrapper(origv)) MOZ_CRASH("wrapper found for target object"); } } /* * Brain transplants. Not for beginners or the squeamish. * @@ -949,17 +949,17 @@ JS_PUBLIC_API(bool) JS_RefreshCrossCompartmentWrappers(JSContext* cx, HandleObject obj) { return RemapAllWrappersForObject(cx, obj, obj); } JS_PUBLIC_API(bool) JS_InitStandardClasses(JSContext* cx, HandleObject obj) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); Rooted<GlobalObject*> global(cx, &obj->global()); return GlobalObject::initStandardClasses(cx, global); } @@ -1969,34 +1969,34 @@ JS_FireOnNewGlobalObject(JSContext* cx, assertSameCompartment(cx, global); Rooted<js::GlobalObject*> globalObject(cx, &global->as<GlobalObject>()); Debugger::onNewGlobalObject(cx, globalObject); } JS_PUBLIC_API(JSObject*) JS_NewObject(JSContext* cx, const JSClass* jsclasp) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); const Class* clasp = Valueify(jsclasp); if (!clasp) clasp = &PlainObject::class_; /* default class is Object */ MOZ_ASSERT(clasp != &JSFunction::class_); MOZ_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL)); return NewObjectWithClassProto(cx, clasp, nullptr); } JS_PUBLIC_API(JSObject*) JS_NewObjectWithGivenProto(JSContext* cx, const JSClass* jsclasp, HandleObject proto) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); assertSameCompartment(cx, proto); const Class* clasp = Valueify(jsclasp); if (!clasp) clasp = &PlainObject::class_; /* default class is Object */ @@ -2004,17 +2004,17 @@ JS_NewObjectWithGivenProto(JSContext* cx MOZ_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL)); return NewObjectWithGivenProto(cx, clasp, proto); } JS_PUBLIC_API(JSObject*) JS_NewPlainObject(JSContext* cx) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); return NewBuiltinClassInstance<PlainObject>(cx); } JS_PUBLIC_API(JSObject*) JS_NewObjectForConstructor(JSContext* cx, const JSClass* clasp, const CallArgs& args) @@ -2904,34 +2904,34 @@ JS::IsConstructor(JSObject* obj) { return obj->isConstructor(); } JS_PUBLIC_API(bool) JS_CallFunctionValue(JSContext* cx, HandleObject obj, HandleValue fval, const HandleValueArray& args, MutableHandleValue rval) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, fval, args); InvokeArgs iargs(cx); if (!FillArgumentsFromArraylike(cx, iargs, args)) return false; RootedValue thisv(cx, ObjectOrNullValue(obj)); return Call(cx, fval, thisv, iargs, rval); } JS_PUBLIC_API(bool) JS_CallFunction(JSContext* cx, HandleObject obj, HandleFunction fun, const HandleValueArray& args, MutableHandleValue rval) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, fun, args); InvokeArgs iargs(cx); if (!FillArgumentsFromArraylike(cx, iargs, args)) return false; @@ -2939,17 +2939,17 @@ JS_CallFunction(JSContext* cx, HandleObj RootedValue thisv(cx, ObjectOrNullValue(obj)); return Call(cx, fval, thisv, iargs, rval); } JS_PUBLIC_API(bool) JS_CallFunctionName(JSContext* cx, HandleObject obj, const char* name, const HandleValueArray& args, MutableHandleValue rval) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, args); JSAtom* atom = Atomize(cx, name, strlen(name)); if (!atom) return false; @@ -3373,28 +3373,28 @@ JS_PUBLIC_API(void) JS_SetReservedSlot(JSObject* obj, uint32_t index, const Value& value) { obj->as<NativeObject>().setReservedSlot(index, value); } JS_PUBLIC_API(JSObject*) JS_NewArrayObject(JSContext* cx, const JS::HandleValueArray& contents) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); assertSameCompartment(cx, contents); return NewDenseCopiedArray(cx, contents.length(), contents.begin()); } JS_PUBLIC_API(JSObject*) JS_NewArrayObject(JSContext* cx, size_t length) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); return NewDenseFullyAllocatedArray(cx, length); } inline bool IsGivenTypeObject(JSContext* cx, JS::HandleObject obj, const ESClass& typeClass, bool* isType) @@ -3507,17 +3507,17 @@ JS_InitReadPrincipalsCallback(JSContext* MOZ_ASSERT(!cx->runtime()->readPrincipals); cx->runtime()->readPrincipals = read; } JS_PUBLIC_API(JSFunction*) JS_NewFunction(JSContext* cx, JSNative native, unsigned nargs, unsigned flags, const char* name) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); RootedAtom atom(cx); if (name) { atom = Atomize(cx, name, strlen(name)); if (!atom) @@ -3527,17 +3527,17 @@ JS_NewFunction(JSContext* cx, JSNative n return (flags & JSFUN_CONSTRUCTOR) ? NewNativeConstructor(cx, native, nargs, atom) : NewNativeFunction(cx, native, nargs, atom); } JS_PUBLIC_API(JSFunction*) JS::GetSelfHostedFunction(JSContext* cx, const char* selfHostedName, HandleId id, unsigned nargs) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); assertSameCompartment(cx, id); RootedAtom name(cx, IdToFunctionName(cx, id)); if (!name) return nullptr; @@ -3783,60 +3783,60 @@ extern JS_PUBLIC_API(bool) JS_IsConstructor(JSFunction* fun) { return fun->isConstructor(); } JS_PUBLIC_API(bool) JS_DefineFunctions(JSContext* cx, HandleObject obj, const JSFunctionSpec* fs) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); return DefineFunctions(cx, obj, fs, NotIntrinsic); } JS_PUBLIC_API(JSFunction*) JS_DefineFunction(JSContext* cx, HandleObject obj, const char* name, JSNative call, unsigned nargs, unsigned attrs) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); JSAtom* atom = Atomize(cx, name, strlen(name)); if (!atom) return nullptr; Rooted<jsid> id(cx, AtomToId(atom)); return DefineFunction(cx, obj, id, call, nargs, attrs); } JS_PUBLIC_API(JSFunction*) JS_DefineUCFunction(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen, JSNative call, unsigned nargs, unsigned attrs) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen)); if (!atom) return nullptr; Rooted<jsid> id(cx, AtomToId(atom)); return DefineFunction(cx, obj, id, call, nargs, attrs); } extern JS_PUBLIC_API(JSFunction*) JS_DefineFunctionById(JSContext* cx, HandleObject obj, HandleId id, JSNative call, unsigned nargs, unsigned attrs) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); assertSameCompartment(cx, obj, id); return DefineFunction(cx, obj, id, call, nargs, attrs); } /* Use the fastest available getc. */ #if defined(HAVE_GETC_UNLOCKED) @@ -4077,17 +4077,17 @@ JS::CompileOptions::CompileOptions(JSCon } static bool Compile(JSContext* cx, const ReadOnlyCompileOptions& options, SourceBufferHolder& srcBuf, MutableHandleScript script) { ScopeKind scopeKind = options.nonSyntacticScope ? ScopeKind::NonSyntactic : ScopeKind::Global; - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); script.set(frontend::CompileGlobalScript(cx, cx->tempLifoAlloc(), scopeKind, options, srcBuf)); return !!script; } static bool @@ -4218,17 +4218,17 @@ JS::CompileForNonSyntacticScope(JSContex } #if defined(JS_BUILD_BINAST) JSScript* JS::DecodeBinAST(JSContext* cx, const ReadOnlyCompileOptions& options, const uint8_t* buf, size_t length) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); return frontend::CompileGlobalBinASTScript(cx, cx->tempLifoAlloc(), options, buf, length); } JSScript* JS::DecodeBinAST(JSContext* cx, const ReadOnlyCompileOptions& options, FILE* file) @@ -4542,17 +4542,17 @@ JS_GetFunctionScript(JSContext* cx, Hand */ static bool CompileFunction(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, HandleAtom name, bool isInvalidName, SourceBufferHolder& srcBuf, uint32_t parameterListEnd, HandleObject enclosingEnv, HandleScope enclosingScope, MutableHandleFunction fun) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); assertSameCompartment(cx, enclosingEnv); RootedAtom funAtom(cx); fun.set(NewScriptedFunction(cx, 0, JSFunction::INTERPRETED_NORMAL, isInvalidName ? nullptr : name, /* proto = */ nullptr, @@ -4711,17 +4711,17 @@ JS::ExposeScriptToDebugger(JSContext* cx MOZ_ASSERT(script->hideScriptFromDebugger()); script->clearHideScriptFromDebugger(); Debugger::onNewScript(cx, script); } JS_PUBLIC_API(JSString*) JS_DecompileScript(JSContext* cx, HandleScript script) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); script->ensureNonLazyCanonicalFunction(); RootedFunction fun(cx, script->functionNonDelazifying()); if (fun) return JS_DecompileFunction(cx, fun); bool haveSource = script->scriptSource()->hasSourceData(); @@ -4729,27 +4729,27 @@ JS_DecompileScript(JSContext* cx, Handle return nullptr; return haveSource ? JSScript::sourceData(cx, script) : NewStringCopyZ<CanGC>(cx, "[no source]"); } JS_PUBLIC_API(JSString*) JS_DecompileFunction(JSContext* cx, HandleFunction fun) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); assertSameCompartment(cx, fun); return FunctionToString(cx, fun, /* isToSource = */ false); } MOZ_NEVER_INLINE static bool ExecuteScript(JSContext* cx, HandleObject scope, HandleScript script, Value* rval) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); assertSameCompartment(cx, scope, script); MOZ_ASSERT_IF(!IsGlobalLexicalEnvironment(scope), script->hasNonSyntacticScope()); return Execute(cx, script, *scope, rval); } static bool @@ -4833,17 +4833,17 @@ JS::CloneAndExecuteScript(JSContext* cx, } static bool Evaluate(JSContext* cx, ScopeKind scopeKind, HandleObject env, const ReadOnlyCompileOptions& optionsArg, SourceBufferHolder& srcBuf, MutableHandleValue rval) { CompileOptions options(cx, optionsArg); - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); assertSameCompartment(cx, env); MOZ_ASSERT_IF(!IsGlobalLexicalEnvironment(env), scopeKind == ScopeKind::NonSyntactic); options.setIsRunOnce(true); RootedScript script(cx, frontend::CompileGlobalScript(cx, cx->tempLifoAlloc(), scopeKind, options, srcBuf)); @@ -4974,17 +4974,17 @@ JS::SetModuleMetadataHook(JSRuntime* rt, AssertHeapIsIdle(); rt->moduleMetadataHook = func; } JS_PUBLIC_API(bool) JS::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options, SourceBufferHolder& srcBuf, JS::MutableHandleObject module) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); module.set(frontend::CompileModule(cx, options, srcBuf)); return !!module; } JS_PUBLIC_API(void) @@ -5132,17 +5132,17 @@ JS::SetPromiseRejectionTrackerCallback(J { cx->promiseRejectionTrackerCallback = callback; cx->promiseRejectionTrackerCallbackData = data; } JS_PUBLIC_API(JSObject*) JS::NewPromiseObject(JSContext* cx, HandleObject executor, HandleObject proto /* = nullptr */) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); assertSameCompartment(cx, executor, proto); if (!executor) return PromiseObject::createSkippingExecutor(cx); MOZ_ASSERT(IsCallable(executor)); @@ -5386,17 +5386,17 @@ JS::GetWaitForAllPromise(JSContext* cx, JS_PUBLIC_API(JSObject*) JS::NewReadableDefaultStreamObject(JSContext* cx, JS::HandleObject underlyingSource /* = nullptr */, JS::HandleFunction size /* = nullptr */, double highWaterMark /* = 1 */, JS::HandleObject proto /* = nullptr */) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); RootedObject source(cx, underlyingSource); if (!source) { source = NewBuiltinClassInstance<PlainObject>(cx); if (!source) return nullptr; @@ -5408,17 +5408,17 @@ JS::NewReadableDefaultStreamObject(JSCon } JS_PUBLIC_API(JSObject*) JS::NewReadableByteStreamObject(JSContext* cx, JS::HandleObject underlyingSource /* = nullptr */, double highWaterMark /* = 1 */, JS::HandleObject proto /* = nullptr */) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); RootedObject source(cx, underlyingSource); if (!source) { source = NewBuiltinClassInstance<PlainObject>(cx); if (!source) return nullptr; @@ -5467,17 +5467,17 @@ JS::HasReadableStreamCallbacks(JSContext return cx->runtime()->readableStreamDataRequestCallback; } JS_PUBLIC_API(JSObject*) JS::NewReadableExternalSourceStreamObject(JSContext* cx, void* underlyingSource, uint8_t flags /* = 0 */, HandleObject proto /* = nullptr */) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); AssertHeapIsIdle(); CHECK_REQUEST(cx); #ifdef DEBUG JSRuntime* rt = cx->runtime(); MOZ_ASSERT(rt->readableStreamDataRequestCallback); MOZ_ASSERT(rt->readableStreamWriteIntoReadRequestCallback); MOZ_ASSERT(rt->readableStreamCancelCallback); @@ -7622,19 +7622,18 @@ GetScriptedCallerGlobal(JSContext* cx) // If the caller is hidden, the embedding wants us to return null here so // that it can check its own stack (see HideScriptedCaller). if (activation->scriptedCallerIsHidden()) return nullptr; GlobalObject* global = realm->maybeGlobal(); - // No one should be running code in the atoms realm or running code in a - // realm without any live objects, so there should definitely be a live - // global. + // No one should be running code in a realm without any live objects, so + // there should definitely be a live global. MOZ_ASSERT(global); return global; } JS_PUBLIC_API(void) HideScriptedCaller(JSContext* cx) {
--- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -157,21 +157,26 @@ JS_GetIsSecureContext(JSCompartment* com JS_FRIEND_API(JSPrincipals*) JS_GetCompartmentPrincipals(JSCompartment* compartment) { Realm* realm = JS::GetRealmForCompartment(compartment); return realm->principals(); } +JS_FRIEND_API(JSPrincipals*) +JS::GetRealmPrincipals(JS::Realm* realm) +{ + return realm->principals(); +} + JS_FRIEND_API(void) -JS_SetCompartmentPrincipals(JSCompartment* compartment, JSPrincipals* principals) +JS::SetRealmPrincipals(JS::Realm* realm, JSPrincipals* principals) { // Short circuit if there's no change. - Realm* realm = JS::GetRealmForCompartment(compartment); if (principals == realm->principals()) return; // Any realm with the trusted principals -- and there can be // multiple -- is a system realm. const JSPrincipals* trusted = realm->runtimeFromMainThread()->trustedPrincipals(); bool isSystem = principals && principals == trusted; @@ -241,17 +246,17 @@ DefineHelpProperty(JSContext* cx, Handle if (!atom) return false; return JS_DefineProperty(cx, obj, prop, atom, JSPROP_READONLY | JSPROP_PERMANENT); } JS_FRIEND_API(bool) JS_DefineFunctionsWithHelp(JSContext* cx, HandleObject obj, const JSFunctionSpecWithHelp* fs) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); for (; fs->name; fs++) { JSAtom* atom = Atomize(cx, fs->name, strlen(fs->name)); if (!atom) return false; @@ -347,22 +352,16 @@ js::IsSystemCompartment(JSCompartment* c JS_FRIEND_API(bool) js::IsSystemZone(Zone* zone) { return zone->isSystem; } JS_FRIEND_API(bool) -js::IsAtomsRealm(JS::Realm* realm) -{ - return realm->isAtomsRealm(); -} - -JS_FRIEND_API(bool) js::IsAtomsZone(JS::Zone* zone) { return zone->runtimeFromAnyThread()->isAtomsZone(zone); } JS_FRIEND_API(bool) js::IsFunctionObject(JSObject* obj) { @@ -428,31 +427,31 @@ js::RunningWithTrustedPrincipals(JSConte return cx->runningWithTrustedPrincipals(); } JS_FRIEND_API(JSFunction*) js::DefineFunctionWithReserved(JSContext* cx, JSObject* objArg, const char* name, JSNative call, unsigned nargs, unsigned attrs) { RootedObject obj(cx, objArg); - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); CHECK_REQUEST(cx); assertSameCompartment(cx, obj); JSAtom* atom = Atomize(cx, name, strlen(name)); if (!atom) return nullptr; Rooted<jsid> id(cx, AtomToId(atom)); return DefineFunction(cx, obj, id, call, nargs, attrs, gc::AllocKind::FUNCTION_EXTENDED); } JS_FRIEND_API(JSFunction*) js::NewFunctionWithReserved(JSContext* cx, JSNative native, unsigned nargs, unsigned flags, const char* name) { - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); CHECK_REQUEST(cx); RootedAtom atom(cx); if (name) { atom = Atomize(cx, name, strlen(name)); if (!atom) return nullptr; @@ -463,17 +462,17 @@ js::NewFunctionWithReserved(JSContext* c NewNativeFunction(cx, native, nargs, atom, gc::AllocKind::FUNCTION_EXTENDED); } JS_FRIEND_API(JSFunction*) js::NewFunctionByIdWithReserved(JSContext* cx, JSNative native, unsigned nargs, unsigned flags, jsid id) { MOZ_ASSERT(JSID_IS_STRING(id)); - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); CHECK_REQUEST(cx); assertSameCompartment(cx, id); RootedAtom atom(cx, JSID_TO_ATOM(id)); return (flags & JSFUN_CONSTRUCTOR) ? NewNativeConstructor(cx, native, nargs, atom, gc::AllocKind::FUNCTION_EXTENDED) : NewNativeFunction(cx, native, nargs, atom, gc::AllocKind::FUNCTION_EXTENDED); } @@ -1270,16 +1269,19 @@ JS_FRIEND_API(void) JS::NotifyGCRootsRemoved(JSContext* cx) { cx->runtime()->gc.notifyRootsRemoved(); } JS_FRIEND_API(JS::Realm*) js::GetAnyRealmInZone(JS::Zone* zone) { + if (zone->isAtomsZone()) + return nullptr; + RealmsInZoneIter realm(zone); MOZ_ASSERT(!realm.done()); return realm.get(); } void JS::ObjectPtr::finalize(JSRuntime* rt) { @@ -1538,18 +1540,19 @@ AutoAssertNoContentJS::~AutoAssertNoCont JS_FRIEND_API(void) js::EnableAccessValidation(JSContext* cx, bool enabled) { cx->enableAccessValidation = enabled; } JS_FRIEND_API(void) -js::SetCompartmentValidAccessPtr(JSContext* cx, JS::HandleObject global, bool* accessp) +js::SetRealmValidAccessPtr(JSContext* cx, JS::HandleObject global, bool* accessp) { + MOZ_ASSERT(global->is<GlobalObject>()); global->realm()->setValidAccessPtr(accessp); } JS_FRIEND_API(bool) js::SystemZoneAvailable(JSContext* cx) { return true; }
--- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -185,19 +185,16 @@ extern JS_FRIEND_API(void) JS_SetSetUseCounterCallback(JSContext* cx, JSSetUseCounterCallback callback); extern JS_FRIEND_API(bool) JS_GetIsSecureContext(JSCompartment* compartment); extern JS_FRIEND_API(JSPrincipals*) JS_GetCompartmentPrincipals(JSCompartment* compartment); -extern JS_FRIEND_API(void) -JS_SetCompartmentPrincipals(JSCompartment* compartment, JSPrincipals* principals); - extern JS_FRIEND_API(JSPrincipals*) JS_GetScriptPrincipals(JSScript* script); namespace js { extern JS_FRIEND_API(JSCompartment*) GetScriptCompartment(JSScript* script); } /* namespace js */ @@ -320,16 +317,22 @@ ForceLexicalInitialization(JSContext *cx /** * Whether we are poisoning unused/released data for error detection. Governed * by the JS_GC_POISONING #ifdef as well as the $JSGC_DISABLE_POISONING * environment variable. */ extern JS_FRIEND_API(int) IsGCPoisoning(); +extern JS_FRIEND_API(JSPrincipals*) +GetRealmPrincipals(JS::Realm* realm); + +extern JS_FRIEND_API(void) +SetRealmPrincipals(JS::Realm* realm, JSPrincipals* principals); + } // namespace JS /** * Copies all own properties from |obj| to |target|. |obj| must be a "native" * object (that is to say, normal-ish - not an Array or a Proxy). * * This function immediately enters a compartment, and does not impose any * restrictions on the compartment of |cx|. @@ -479,19 +482,16 @@ JS_FRIEND_API(bool) obj_defineSetter(JSC extern JS_FRIEND_API(bool) IsSystemCompartment(JSCompartment* comp); extern JS_FRIEND_API(bool) IsSystemZone(JS::Zone* zone); extern JS_FRIEND_API(bool) -IsAtomsRealm(JS::Realm* realm); - -extern JS_FRIEND_API(bool) IsAtomsZone(JS::Zone* zone); struct WeakMapTracer { JSRuntime* runtime; explicit WeakMapTracer(JSRuntime* rt) : runtime(rt) {} @@ -548,31 +548,32 @@ extern JS_FRIEND_API(bool) CheckGrayMarkingState(JSRuntime* rt); #endif #ifdef JS_HAS_CTYPES extern JS_FRIEND_API(size_t) SizeOfDataIfCDataObject(mozilla::MallocSizeOf mallocSizeOf, JSObject* obj); #endif +// Note: this returns nullptr iff |zone| is the atoms zone. extern JS_FRIEND_API(JS::Realm*) GetAnyRealmInZone(JS::Zone* zone); /* * Shadow declarations of JS internal structures, for access by inline access * functions below. Do not use these structures in any other way. When adding * new fields for access by inline methods, make sure to add static asserts to * the original header file to ensure that offsets are consistent. */ namespace shadow { struct ObjectGroup { const Class* clasp; - JSObject* proto; - JSCompartment* compartment; + JSObject* proto; + JS::Realm* realm; }; struct BaseShape { const js::Class* clasp_; JSObject* parent; }; class Shape { @@ -665,20 +666,33 @@ InheritanceProtoKeyForStandardClass(JSPr // Otherwise, we inherit [Object]. return JSProto_Object; } JS_FRIEND_API(bool) IsFunctionObject(JSObject* obj); +JS_FRIEND_API(bool) +IsCrossCompartmentWrapper(JSObject* obj); + static MOZ_ALWAYS_INLINE JSCompartment* GetObjectCompartment(JSObject* obj) { - return reinterpret_cast<shadow::Object*>(obj)->group->compartment; + JS::Realm* realm = reinterpret_cast<shadow::Object*>(obj)->group->realm; + return JS::GetCompartmentForRealm(realm); +} + +// CrossCompartmentWrappers are shared by all realms within the compartment, so +// getting a wrapper's realm usually doesn't make sense. +static MOZ_ALWAYS_INLINE JS::Realm* +GetNonCCWObjectRealm(JSObject* obj) +{ + MOZ_ASSERT(!js::IsCrossCompartmentWrapper(obj)); + return reinterpret_cast<shadow::Object*>(obj)->group->realm; } JS_FRIEND_API(JSObject*) GetGlobalForObjectCrossCompartment(JSObject* obj); JS_FRIEND_API(JSObject*) GetPrototypeNoProxy(JSObject* obj); @@ -3088,28 +3102,28 @@ class MOZ_STACK_CLASS JS_FRIEND_API(Auto ~AutoAssertNoContentJS(); private: JSContext* context_; bool prevAllowContentJS_; }; // Turn on assertions so that we assert that -// !comp->validAccessPtr || *comp->validAccessPtr -// is true for every |comp| that we run JS code in. The compartment's validAccessPtr -// is set via SetCompartmentValidAccessPtr. +// !realm->validAccessPtr || *realm->validAccessPtr +// is true for every |realm| that we run JS code in. The realm's validAccessPtr +// is set via SetRealmValidAccessPtr. extern JS_FRIEND_API(void) EnableAccessValidation(JSContext* cx, bool enabled); // See EnableAccessValidation above. The caller must guarantee that accessp will // live at least as long as |global| is alive. The JS engine reads accessp from // threads that are allowed to run code on |global|, so all changes to *accessp // should be made from whichever thread owns |global| at a given time. extern JS_FRIEND_API(void) -SetCompartmentValidAccessPtr(JSContext* cx, JS::HandleObject global, bool* accessp); +SetRealmValidAccessPtr(JSContext* cx, JS::HandleObject global, bool* accessp); // Returns true if the system zone is available (i.e., if no cooperative contexts // are using it now). extern JS_FRIEND_API(bool) SystemZoneAvailable(JSContext* cx); typedef void (* LogCtorDtor)(void* self, const char* type, uint32_t sz);
--- a/js/src/jspubtd.h +++ b/js/src/jspubtd.h @@ -80,16 +80,17 @@ class JS_PUBLIC_API(JSTracer); class JSFlatString; template<typename T> struct JSConstScalarSpec; typedef JSConstScalarSpec<double> JSConstDoubleSpec; typedef JSConstScalarSpec<int32_t> JSConstIntegerSpec; namespace js { +inline JS::Realm* GetContextRealm(const JSContext* cx); inline JSCompartment* GetContextCompartment(const JSContext* cx); inline JS::Zone* GetContextZone(const JSContext* cx); // Whether the current thread is permitted access to any part of the specified // runtime or zone. JS_FRIEND_API(bool) CurrentThreadCanAccessRuntime(const JSRuntime* rt);
--- a/js/src/proxy/CrossCompartmentWrapper.cpp +++ b/js/src/proxy/CrossCompartmentWrapper.cpp @@ -532,17 +532,17 @@ js::NukeCrossCompartmentWrappers(JSConte const CompartmentFilter& sourceFilter, JSCompartment* target, js::NukeReferencesToWindow nukeReferencesToWindow, js::NukeReferencesFromTarget nukeReferencesFromTarget) { CHECK_REQUEST(cx); JSRuntime* rt = cx->runtime(); - for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { + for (CompartmentsIter c(rt); !c.done(); c.next()) { if (!sourceFilter.match(c)) continue; // If the compartment matches both the source and target filter, we may // want to cut both incoming and outgoing wrappers. bool nukeAll = (nukeReferencesFromTarget == NukeAllReferences && target == c.get()); @@ -676,17 +676,17 @@ js::RemapAllWrappersForObject(JSContext* RootedValue origv(cx, ObjectValue(*oldTargetArg)); RootedObject newTarget(cx, newTargetArg); AutoWrapperVector toTransplant(cx); if (!toTransplant.reserve(cx->runtime()->numCompartments)) return false; - for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) { + for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) { if (WrapperMap::Ptr wp = c->lookupWrapper(origv)) { // We found a wrapper. Remember and root it. toTransplant.infallibleAppend(WrapperValue(wp)); } } for (const WrapperValue& v : toTransplant) RemapWrapper(cx, &v.toObject(), newTarget); @@ -696,17 +696,17 @@ js::RemapAllWrappersForObject(JSContext* JS_FRIEND_API(bool) js::RecomputeWrappers(JSContext* cx, const CompartmentFilter& sourceFilter, const CompartmentFilter& targetFilter) { bool evictedNursery = false; AutoWrapperVector toRecompute(cx); - for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) { + for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) { // Filter by source compartment. if (!sourceFilter.match(c)) continue; if (!evictedNursery && c->hasNurseryAllocatedWrapperEntries(targetFilter)) { cx->runtime()->gc.evictNursery(); evictedNursery = true; }
--- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -8904,17 +8904,17 @@ Shell(JSContext* cx, OptionParser* op, c JS_free(cx, const_cast<char*>(jsCacheDir)); } } /* * Dump remaining type inference results while we still have a context. * This printing depends on atoms still existing. */ - for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) + for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) PrintTypes(cx, c, false); return result; } static void SetOutputFile(const char* const envVar, RCFile* defaultOut,
--- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -3059,17 +3059,17 @@ Debugger::markIteratively(GCMarker* mark { bool markedAny = false; /* * Find all Debugger objects in danger of GC. This code is a little * convoluted since the easiest way to find them is via their debuggees. */ JSRuntime* rt = marker->runtime(); - for (RealmsIter r(rt, SkipAtoms); !r.done(); r.next()) { + for (RealmsIter r(rt); !r.done(); r.next()) { if (r->isDebuggee()) { GlobalObject* global = r->unsafeUnbarrieredMaybeGlobal(); if (!IsMarkedUnbarriered(rt, &global)) continue; /* * Every debuggee has at least one debugger, so in this case * getDebuggers can't return nullptr. @@ -3256,36 +3256,49 @@ Debugger::detachAllDebuggersFromGlobal(F MOZ_ASSERT(!debuggers->empty()); while (!debuggers->empty()) debuggers->back()->removeDebuggeeGlobal(fop, global, nullptr); } /* static */ void Debugger::findZoneEdges(Zone* zone, js::gc::ZoneComponentFinder& finder) { - /* - * For debugger cross compartment wrappers, add edges in the opposite - * direction to those already added by JSCompartment::findOutgoingEdges. - * This ensure that debuggers and their debuggees are finalized in the same - * group. - */ JSRuntime* rt = zone->runtimeFromMainThread(); for (Debugger* dbg : rt->debuggerList()) { - Zone* w = dbg->object->zone(); - if (w == zone || !w->isGCMarking()) + Zone* debuggerZone = dbg->object->zone(); + if (!debuggerZone->isGCMarking()) continue; - if (dbg->debuggeeZones.has(zone) || - dbg->scripts.hasKeyInZone(zone) || - dbg->sources.hasKeyInZone(zone) || - dbg->objects.hasKeyInZone(zone) || - dbg->environments.hasKeyInZone(zone) || - dbg->wasmInstanceScripts.hasKeyInZone(zone) || - dbg->wasmInstanceSources.hasKeyInZone(zone)) - { - finder.addEdgeTo(w); + + if (debuggerZone == zone) { + /* + * Add edges to debuggee zones. These are weak references that are + * not in the cross compartment wrapper map. + */ + for (auto e = dbg->debuggeeZones.all(); !e.empty(); e.popFront()) { + Zone* debuggeeZone = e.front(); + if (debuggeeZone->isGCMarking()) + finder.addEdgeTo(debuggeeZone); + } + } else { + /* + * For debugger cross compartment wrappers, add edges in the + * opposite direction to those already added by + * JSCompartment::findOutgoingEdges and above. This ensure that + * debuggers and their debuggees are finalized in the same group. + */ + if (dbg->debuggeeZones.has(zone) || + dbg->scripts.hasKeyInZone(zone) || + dbg->sources.hasKeyInZone(zone) || + dbg->objects.hasKeyInZone(zone) || + dbg->environments.hasKeyInZone(zone) || + dbg->wasmInstanceScripts.hasKeyInZone(zone) || + dbg->wasmInstanceSources.hasKeyInZone(zone)) + { + finder.addEdgeTo(debuggerZone); + } } } } const ClassOps Debugger::classOps_ = { nullptr, /* addProperty */ nullptr, /* delProperty */ nullptr, /* enumerate */ @@ -4952,17 +4965,17 @@ Debugger::findAllGlobals(JSContext* cx, AutoObjectVector globals(cx); { // Accumulate the list of globals before wrapping them, because // wrapping can GC and collect realms from under us, while iterating. JS::AutoCheckCannotGC nogc; - for (RealmsIter r(cx->runtime(), SkipAtoms); !r.done(); r.next()) { + for (RealmsIter r(cx->runtime()); !r.done(); r.next()) { if (r->creationOptions().invisibleToDebugger()) continue; r->compartment()->scheduledForDestruction = false; GlobalObject* global = r->maybeGlobal(); if (cx->runtime()->isSelfHostingGlobal(global))
--- a/js/src/vm/GeckoProfiler.cpp +++ b/js/src/vm/GeckoProfiler.cpp @@ -147,17 +147,17 @@ GeckoProfilerRuntime::enable(bool enable jitActivation = jitActivation->prevJitActivation(); } } } // WebAssembly code does not need to be released, but profiling string // labels have to be generated so that they are available during async // profiling stack iteration. - for (RealmsIter r(rt, SkipAtoms); !r.done(); r.next()) + for (RealmsIter r(rt); !r.done(); r.next()) r->wasm.ensureProfilingLabels(enabled); } /* Lookup the string for the function/script, creating one if necessary */ const char* GeckoProfilerRuntime::profileString(JSScript* script, JSFunction* maybeFun) { auto locked = strings.lock();
--- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -499,17 +499,17 @@ GlobalObject::createInternal(JSContext* } /* static */ GlobalObject* GlobalObject::new_(JSContext* cx, const Class* clasp, JSPrincipals* principals, JS::OnNewGlobalHookOption hookOption, const JS::RealmOptions& options) { MOZ_ASSERT(!cx->isExceptionPending()); - MOZ_ASSERT_IF(cx->realm(), !cx->realm()->isAtomsRealm()); + MOZ_ASSERT_IF(cx->zone(), !cx->zone()->isAtomsZone()); Realm* realm = NewRealm(cx, principals, options); if (!realm) return nullptr; Rooted<GlobalObject*> global(cx); { AutoRealmUnchecked ar(cx, realm);
--- a/js/src/vm/HelperThreads.cpp +++ b/js/src/vm/HelperThreads.cpp @@ -342,17 +342,17 @@ js::CancelOffThreadIonCompile(const Comp } #ifdef DEBUG bool js::HasOffThreadIonCompile(Realm* realm) { AutoLockHelperThreadState lock; - if (!HelperThreadState().threads || realm->isAtomsRealm()) + if (!HelperThreadState().threads) return false; GlobalHelperThreadState::IonBuilderVector& worklist = HelperThreadState().ionWorklist(lock); for (size_t i = 0; i < worklist.length(); i++) { jit::IonBuilder* builder = worklist[i]; if (builder->script()->realm() == realm) return true; } @@ -748,17 +748,17 @@ CreateGlobalForOffThreadParse(JSContext* JSObject* obj = JS_NewGlobalObject(cx, &parseTaskGlobalClass, nullptr, JS::DontFireOnNewGlobalHook, realmOptions); if (!obj) return nullptr; Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>()); - JS_SetCompartmentPrincipals(global->compartment(), currentRealm->principals()); + JS::SetRealmPrincipals(global->realm(), currentRealm->principals()); return global; } static bool QueueOffThreadParseTask(JSContext* cx, ParseTask* task) { AutoLockHelperThreadState lock;
--- a/js/src/vm/JSAtom.cpp +++ b/js/src/vm/JSAtom.cpp @@ -446,17 +446,17 @@ AtomizeAndCopyCharsInner(JSContext* cx, atom->setPinned(); p->setPinned(true); } return atom; } JSAtom* atom; { - AutoAtomsRealm ar(cx, lock); + AutoAtomsZone az(cx, lock); JSFlatString* flat = NewStringCopyN<NoGC>(cx, tbchars, length); if (!flat) { // Grudgingly forgo last-ditch GC. The alternative would be to release // the lock, manually GC here, and retry from the top. If you fix this, // please also fix or comment the similar case in Symbol::new_. ReportOutOfMemory(cx); return nullptr;
--- a/js/src/vm/JSCompartment-inl.h +++ b/js/src/vm/JSCompartment-inl.h @@ -47,54 +47,46 @@ JS::Realm::globalIsAboutToBeFinalized() js::ObjectRealm::get(const JSObject* obj) { return obj->realm()->objects_; } template <typename T> js::AutoRealm::AutoRealm(JSContext* cx, const T& target) : cx_(cx), - origin_(cx->realm()), - maybeLock_(nullptr) + origin_(cx->realm()) { cx_->enterRealmOf(target); } -// Protected constructor that bypasses assertions in enterCompartmentOf. Used -// only for entering the atoms realm. -js::AutoRealm::AutoRealm(JSContext* cx, JS::Realm* target, - js::AutoLockForExclusiveAccess& lock) - : cx_(cx), - origin_(cx->realm()), - maybeLock_(&lock) -{ - MOZ_ASSERT(target->isAtomsRealm()); - cx_->enterAtomsRealm(target, lock); -} - -// Protected constructor that bypasses assertions in enterCompartmentOf. Should -// not be used to enter the atoms realm. +// Protected constructor that bypasses assertions in enterRealmOf. js::AutoRealm::AutoRealm(JSContext* cx, JS::Realm* target) : cx_(cx), - origin_(cx->realm()), - maybeLock_(nullptr) + origin_(cx->realm()) { - MOZ_ASSERT(!target->isAtomsRealm()); - cx_->enterNonAtomsRealm(target); + cx_->enterRealm(target); } js::AutoRealm::~AutoRealm() { - cx_->leaveRealm(origin_, maybeLock_); + cx_->leaveRealm(origin_); } -js::AutoAtomsRealm::AutoAtomsRealm(JSContext* cx, - js::AutoLockForExclusiveAccess& lock) - : AutoRealm(cx, cx->atomsRealm(lock), lock) -{} +js::AutoAtomsZone::AutoAtomsZone(JSContext* cx, js::AutoLockForExclusiveAccess& lock) + : cx_(cx), + origin_(cx->realm()), + lock_(lock) +{ + cx_->enterAtomsZone(lock); +} + +js::AutoAtomsZone::~AutoAtomsZone() +{ + cx_->leaveAtomsZone(origin_, lock_); +} js::AutoRealmUnchecked::AutoRealmUnchecked(JSContext* cx, JS::Realm* target) : AutoRealm(cx, target) {} inline bool JSCompartment::wrap(JSContext* cx, JS::MutableHandleValue vp) {
--- a/js/src/vm/JSCompartment.cpp +++ b/js/src/vm/JSCompartment.cpp @@ -87,72 +87,66 @@ Realm::~Realm() } JSCompartment::~JSCompartment() { runtime_->numCompartments--; } bool -JSCompartment::init(JSContext* maybecx) +JSCompartment::init(JSContext* cx) { if (!crossCompartmentWrappers.init(0)) { - if (maybecx) - ReportOutOfMemory(maybecx); + ReportOutOfMemory(cx); return false; } return true; } bool -ObjectRealm::init(JSContext* maybecx) +ObjectRealm::init(JSContext* cx) { if (!iteratorCache.init()) { - if (maybecx) - ReportOutOfMemory(maybecx); + ReportOutOfMemory(cx); return false; } - NativeIteratorSentinel sentinel(NativeIterator::allocateSentinel(maybecx)); + NativeIteratorSentinel sentinel(NativeIterator::allocateSentinel(cx)); if (!sentinel) return false; iteratorSentinel_ = Move(sentinel); enumerators = iteratorSentinel_.get(); return true; } bool -Realm::init(JSContext* maybecx) +Realm::init(JSContext* cx) { // Initialize JSCompartment. This is temporary until Realm and // JSCompartment are completely separated. - if (!JSCompartment::init(maybecx)) + if (!JSCompartment::init(cx)) return false; /* - * maybecx is null when called to create the atoms realm from - * JSRuntime::init(). - * * As a hack, we clear our timezone cache every time we create a new realm. * This ensures that the cache is always relatively fresh, but shouldn't * interfere with benchmarks that create tons of date objects (unless they * also create tons of iframes, which seems unlikely). */ JS::ResetTimeZone(); - if (!objects_.init(maybecx)) + if (!objects_.init(cx)) return false; if (!savedStacks_.init() || !varNames_.init()) { - if (maybecx) - ReportOutOfMemory(maybecx); + ReportOutOfMemory(cx); return false; } return true; } jit::JitRuntime* JSRuntime::createJitRuntime(JSContext* cx) @@ -302,17 +296,16 @@ CopyStringPure(JSContext* cx, JSString* return nullptr; return NewStringDontDeflate<CanGC>(cx, copiedChars.forget(), len); } bool JSCompartment::wrap(JSContext* cx, MutableHandleString strp) { - MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(this)); MOZ_ASSERT(cx->compartment() == this); /* If the string is already in this compartment, we are done. */ JSString* str = strp; if (str->zoneFromAnyThread() == zone()) return true; /* @@ -449,17 +442,16 @@ JSCompartment::getOrCreateWrapper(JSCont obj.set(wrapper); return true; } bool JSCompartment::wrap(JSContext* cx, MutableHandleObject obj) { - MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(this)); MOZ_ASSERT(cx->compartment() == this); if (!obj) return true; AutoDisableProxyCheck adpc; // Anything we're wrapping has already escaped into script, so must have @@ -481,17 +473,16 @@ JSCompartment::wrap(JSContext* cx, Mutab // Ensure that the wrapper is also exposed. ExposeObjectToActiveJS(obj); return true; } bool JSCompartment::rewrap(JSContext* cx, MutableHandleObject obj, HandleObject existingArg) { - MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(this)); MOZ_ASSERT(cx->compartment() == this); MOZ_ASSERT(obj); MOZ_ASSERT(existingArg); MOZ_ASSERT(existingArg->compartment() == cx->compartment()); MOZ_ASSERT(IsDeadProxyObject(existingArg)); AutoDisableProxyCheck adpc; @@ -610,17 +601,16 @@ ObjectRealm::getNonSyntacticLexicalEnvir return nullptr; return &lexicalEnv->as<LexicalEnvironmentObject>(); } bool Realm::addToVarNames(JSContext* cx, JS::Handle<JSAtom*> name) { MOZ_ASSERT(name); - MOZ_ASSERT(!isAtomsRealm()); if (varNames_.put(name)) return true; ReportOutOfMemory(cx); return false; } @@ -644,17 +634,17 @@ JSCompartment::traceOutgoingCrossCompart } } /* static */ void JSCompartment::traceIncomingCrossCompartmentEdgesForZoneGC(JSTracer* trc) { gcstats::AutoPhase ap(trc->runtime()->gc.stats(), gcstats::PhaseKind::MARK_CCWS); MOZ_ASSERT(JS::CurrentThreadIsHeapMajorCollecting()); - for (CompartmentsIter c(trc->runtime(), SkipAtoms); !c.done(); c.next()) { + for (CompartmentsIter c(trc->runtime()); !c.done(); c.next()) { if (!c->zone()->isCollecting()) c->traceOutgoingCrossCompartmentWrappers(trc); } Debugger::traceIncomingCrossCompartmentEdges(trc); } void Realm::traceGlobal(JSTracer* trc) @@ -914,17 +904,17 @@ Realm::sweepTemplateObjects() iterResultTemplate_.set(nullptr); } /* static */ void JSCompartment::fixupCrossCompartmentWrappersAfterMovingGC(JSTracer* trc) { MOZ_ASSERT(trc->runtime()->gc.isHeapCompacting()); - for (CompartmentsIter comp(trc->runtime(), SkipAtoms); !comp.done(); comp.next()) { + for (CompartmentsIter comp(trc->runtime()); !comp.done(); comp.next()) { // Sweep the wrapper map to update keys (wrapped values) in other // compartments that may have been moved. comp->sweepCrossCompartmentWrappers(); // Trace the wrappers in the map to update their cross-compartment edges // to wrapped values in other compartments that may have been moved. comp->traceOutgoingCrossCompartmentWrappers(trc); } } @@ -1397,22 +1387,16 @@ Realm::addSizeOfIncludingThis(mozilla::M if (scriptCountsMap) { *scriptCountsMapArg += scriptCountsMap->sizeOfIncludingThis(mallocSizeOf); for (auto r = scriptCountsMap->all(); !r.empty(); r.popFront()) { *scriptCountsMapArg += r.front().value()->sizeOfIncludingThis(mallocSizeOf); } } } -HashNumber -Realm::randomHashCode() -{ - return HashNumber(getOrCreateRandomNumberGenerator().next()); -} - mozilla::HashCodeScrambler Realm::randomHashCodeScrambler() { return mozilla::HashCodeScrambler(randomKeyGenerator_.next(), randomKeyGenerator_.next()); } AutoSetNewObjectMetadata::AutoSetNewObjectMetadata(JSContext* cx
--- a/js/src/vm/JSCompartment.h +++ b/js/src/vm/JSCompartment.h @@ -615,17 +615,17 @@ struct JSCompartment private: bool getNonWrapperObjectForCurrentCompartment(JSContext* cx, js::MutableHandleObject obj); bool getOrCreateWrapper(JSContext* cx, js::HandleObject existing, js::MutableHandleObject obj); protected: explicit JSCompartment(JS::Zone* zone); ~JSCompartment(); - MOZ_MUST_USE bool init(JSContext* maybecx); + MOZ_MUST_USE bool init(JSContext* cx); public: MOZ_MUST_USE inline bool wrap(JSContext* cx, JS::MutableHandleValue vp); MOZ_MUST_USE bool wrap(JSContext* cx, js::MutableHandleString strp); #ifdef ENABLE_BIGINT MOZ_MUST_USE bool wrap(JSContext* cx, js::MutableHandle<JS::BigInt*> bi); #endif @@ -726,17 +726,17 @@ class ObjectRealm js::SystemAllocPolicy>; IteratorCache iteratorCache; static inline ObjectRealm& get(const JSObject* obj); explicit ObjectRealm(JS::Zone* zone); ~ObjectRealm(); - MOZ_MUST_USE bool init(JSContext* maybecx); + MOZ_MUST_USE bool init(JSContext* cx); void finishRoots(); void trace(JSTracer* trc); void sweepAfterMinorGC(); void sweepNativeIterators(); void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t* innerViewsArg, @@ -827,17 +827,16 @@ class JS::Realm : private JSCompartment static const unsigned DebuggerObservesMask = IsDebuggee | DebuggerObservesAllExecution | DebuggerObservesCoverage | DebuggerObservesAsmJS | DebuggerObservesBinarySource; unsigned debugModeBits_ = 0; friend class js::AutoRestoreRealmDebugMode; - bool isAtomsRealm_ = false; bool isSelfHostingRealm_ = false; bool marked_ = true; bool isSystem_ = false; public: // WebAssembly state for the realm. js::wasm::Realm wasm; @@ -885,17 +884,17 @@ class JS::Realm : private JSCompartment Realm(const Realm&) = delete; void operator=(const Realm&) = delete; public: Realm(JS::Zone* zone, const JS::RealmOptions& options); ~Realm(); - MOZ_MUST_USE bool init(JSContext* maybecx); + MOZ_MUST_USE bool init(JSContext* cx); void destroy(js::FreeOp* fop); void clearTables(); void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t* tiAllocationSiteTables, size_t* tiArrayTypeTables, size_t* tiObjectTypeTables, size_t* realmObject, @@ -935,35 +934,28 @@ class JS::Realm : private JSCompartment const JS::RealmCreationOptions& creationOptions() const { return creationOptions_; } JS::RealmBehaviors& behaviors() { return behaviors_; } const JS::RealmBehaviors& behaviors() const { return behaviors_; } /* Whether to preserve JIT code on non-shrinking GCs. */ bool preserveJitCode() { return creationOptions_.preserveJitCode(); } - bool isAtomsRealm() const { - return isAtomsRealm_; - } - void setIsAtomsRealm() { - isAtomsRealm_ = true; - } - bool isSelfHostingRealm() const { return isSelfHostingRealm_; } void setIsSelfHostingRealm() { isSelfHostingRealm_ = true; } /* The global object for this realm. * - * This returns nullptr if this is the atoms realm. (The global_ field is - * also null briefly during GC, after the global object is collected; but - * when that happens the Realm is destroyed during the same GC.) + * Note: the global_ field is null briefly during GC, after the global + * object is collected; but when that happens the Realm is destroyed during + * the same GC.) * * In contrast, JSObject::global() is infallible because marking a JSObject * always marks its global as well. */ inline js::GlobalObject* maybeGlobal() const; /* An unbarriered getter for use while tracing. */ inline js::GlobalObject* unsafeUnbarrieredMaybeGlobal() const; @@ -1260,18 +1252,16 @@ class JS::Realm : private JSCompartment // Initializes randomNumberGenerator if needed. mozilla::non_crypto::XorShift128PlusRNG& getOrCreateRandomNumberGenerator(); const void* addressOfRandomNumberGenerator() const { return randomNumberGenerator_.ptr(); } - js::HashNumber randomHashCode(); - mozilla::HashCodeScrambler randomHashCodeScrambler(); bool isAccessValid() const { return validAccessPtr_ ? *validAccessPtr_ : true; } void setValidAccessPtr(bool* accessp) { validAccessPtr_ = accessp; } @@ -1356,42 +1346,45 @@ class MOZ_RAII AssertRealmUnchanged JS::Realm* const oldRealm; MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; class AutoRealm { JSContext* const cx_; JS::Realm* const origin_; - const AutoLockForExclusiveAccess* maybeLock_; public: template <typename T> inline AutoRealm(JSContext* cx, const T& target); inline ~AutoRealm(); JSContext* context() const { return cx_; } JS::Realm* origin() const { return origin_; } protected: inline AutoRealm(JSContext* cx, JS::Realm* target); - // Used only for entering the atoms realm. - inline AutoRealm(JSContext* cx, JS::Realm* target, - AutoLockForExclusiveAccess& lock); - private: AutoRealm(const AutoRealm&) = delete; AutoRealm& operator=(const AutoRealm&) = delete; }; -class AutoAtomsRealm : protected AutoRealm +class MOZ_RAII AutoAtomsZone { + JSContext* const cx_; + JS::Realm* const origin_; + const AutoLockForExclusiveAccess& lock_; + + AutoAtomsZone(const AutoAtomsZone&) = delete; + AutoAtomsZone& operator=(const AutoAtomsZone&) = delete; + public: - inline AutoAtomsRealm(JSContext* cx, AutoLockForExclusiveAccess& lock); + inline AutoAtomsZone(JSContext* cx, AutoLockForExclusiveAccess& lock); + inline ~AutoAtomsZone(); }; // Enter a realm directly. Only use this where there's no target GC thing // to pass to AutoRealm or where you need to avoid the assertions in // JS::Compartment::enterCompartmentOf(). class AutoRealmUnchecked : protected AutoRealm { public:
--- a/js/src/vm/JSContext-inl.h +++ b/js/src/vm/JSContext-inl.h @@ -39,29 +39,24 @@ class CompartmentChecker MOZ_CRASH(); } static void fail(JS::Zone* z1, JS::Zone* z2) { printf("*** Zone mismatch %p vs. %p\n", (void*) z1, (void*) z2); MOZ_CRASH(); } - /* Note: should only be used when neither c1 nor c2 may be the atoms compartment. */ static void check(JSCompartment* c1, JSCompartment* c2) { - MOZ_ASSERT(!c1->runtimeFromAnyThread()->isAtomsCompartment(c1)); - MOZ_ASSERT(!c2->runtimeFromAnyThread()->isAtomsCompartment(c2)); if (c1 != c2) fail(c1, c2); } void check(JSCompartment* c) { - if (c && !compartment->runtimeFromAnyThread()->isAtomsCompartment(c)) { - if (c != compartment) - fail(compartment, c); - } + if (c && c != compartment) + fail(compartment, c); } void checkZone(JS::Zone* z) { if (compartment && z != compartment->zone()) fail(compartment->zone(), z); } void check(JSObject* obj) { @@ -458,88 +453,91 @@ JSContext::setPendingException(const js: inline bool JSContext::runningWithTrustedPrincipals() { return !realm() || realm()->principals() == runtime()->trustedPrincipals(); } inline void -JSContext::enterNonAtomsRealm(JS::Realm* realm) +JSContext::enterRealm(JS::Realm* realm) { - enterRealmDepth_++; + // We should never enter a realm while in the atoms zone. + MOZ_ASSERT_IF(zone(), !zone()->isAtomsZone()); - MOZ_ASSERT(!realm->zone()->isAtomsZone()); - +#ifdef DEBUG + enterRealmDepth_++; +#endif realm->enter(); - setRealm(realm, nullptr); + setRealm(realm); } inline void -JSContext::enterAtomsRealm(JS::Realm* realm, - const js::AutoLockForExclusiveAccess& lock) +JSContext::enterAtomsZone(const js::AutoLockForExclusiveAccess& lock) { - enterRealmDepth_++; + // Only one thread can be in the atoms zone at a time. + MOZ_ASSERT(runtime_->currentThreadHasExclusiveAccess()); - MOZ_ASSERT(realm->zone()->isAtomsZone()); - - realm->enter(); - setRealm(realm, &lock); + realm_ = nullptr; + zone_ = runtime_->atomsZone(lock); + arenas_ = &zone_->arenas; } template <typename T> inline void JSContext::enterRealmOf(const T& target) { MOZ_ASSERT(JS::CellIsNotGray(target)); - enterNonAtomsRealm(target->realm()); + enterRealm(target->realm()); } inline void JSContext::enterNullRealm() { + // We should never enter a realm while in the atoms zone. + MOZ_ASSERT_IF(zone(), !zone()->isAtomsZone()); + +#ifdef DEBUG enterRealmDepth_++; +#endif setRealm(nullptr); } inline void -JSContext::leaveRealm(JS::Realm* oldRealm, - const js::AutoLockForExclusiveAccess* maybeLock /* = nullptr */) +JSContext::leaveRealm(JS::Realm* oldRealm) { MOZ_ASSERT(hasEnteredRealm()); +#ifdef DEBUG enterRealmDepth_--; +#endif // Only call leave() after we've setRealm()-ed away from the current realm. JS::Realm* startingRealm = realm_; - setRealm(oldRealm, maybeLock); + setRealm(oldRealm); if (startingRealm) startingRealm->leave(); } inline void -JSContext::setRealm(JS::Realm* realm, - const js::AutoLockForExclusiveAccess* maybeLock /* = nullptr */) +JSContext::leaveAtomsZone(JS::Realm* oldRealm, + const js::AutoLockForExclusiveAccess& lock) { - // Only one thread can be in the atoms realm at a time. - MOZ_ASSERT_IF(realm && realm->isAtomsRealm(), maybeLock != nullptr); - MOZ_ASSERT_IF((realm && realm->isAtomsRealm()) || (realm_ && realm_->isAtomsRealm()), - runtime_->currentThreadHasExclusiveAccess()); + setRealm(oldRealm); +} - // Make sure that the atoms realm has its own zone. - MOZ_ASSERT_IF(realm && !realm->isAtomsRealm(), - !realm->zone()->isAtomsZone()); - +inline void +JSContext::setRealm(JS::Realm* realm) +{ // Both the current and the new realm should be properly marked as // entered at this point. MOZ_ASSERT_IF(realm_, realm_->hasBeenEntered()); MOZ_ASSERT_IF(realm, realm->hasBeenEntered()); // This thread must have exclusive access to the zone. - MOZ_ASSERT_IF(realm && !realm->zone()->isAtomsZone(), - CurrentThreadCanAccessZone(realm->zone())); + MOZ_ASSERT_IF(realm, CurrentThreadCanAccessZone(realm->zone())); realm_ = realm; zone_ = realm ? realm->zone() : nullptr; arenas_ = zone_ ? &zone_->arenas : nullptr; } inline JSScript* JSContext::currentScript(jsbytecode** ppc,
--- a/js/src/vm/JSContext.cpp +++ b/js/src/vm/JSContext.cpp @@ -1214,17 +1214,19 @@ JSContext::alreadyReportedError() } JSContext::JSContext(JSRuntime* runtime, const JS::ContextOptions& options) : runtime_(runtime), kind_(ContextKind::HelperThread), helperThread_(nullptr), options_(options), arenas_(nullptr), +#ifdef DEBUG enterRealmDepth_(0), +#endif jitActivation(nullptr), activation_(nullptr), profilingActivation_(nullptr), nativeStackBase(GetNativeStackBase()), entryMonitor(nullptr), noExecuteDebuggerTop(nullptr), activityCallback(nullptr), activityCallbackArg(nullptr), @@ -1350,17 +1352,17 @@ JSContext::setRuntime(JSRuntime* rt) runtime_ = rt; } bool JSContext::getPendingException(MutableHandleValue rval) { MOZ_ASSERT(throwing); rval.set(unwrappedException()); - if (realm()->isAtomsRealm()) + if (zone()->isAtomsZone()) return true; bool wasOverRecursed = overRecursed_; clearPendingException(); if (!compartment()->wrap(this, rval)) return false; assertSameCompartment(this, rval); setPendingException(rval); overRecursed_ = wasOverRecursed; @@ -1471,16 +1473,24 @@ JSContext::sizeOfExcludingThis(mozilla:: /* * There are other JSContext members that could be measured; the following * ones have been found by DMD to be worth measuring. More stuff may be * added later. */ return cycleDetectorVector().sizeOfExcludingThis(mallocSizeOf); } +#ifdef DEBUG +bool +JSContext::inAtomsZone() const +{ + return zone_->isAtomsZone(); +} +#endif + void JSContext::trace(JSTracer* trc) { cycleDetectorVector().trace(trc); geckoProfiler().trace(trc); if (trc->isMarkingTracer() && realm_) realm_->mark();
--- a/js/src/vm/JSContext.h +++ b/js/src/vm/JSContext.h @@ -20,16 +20,17 @@ #include "vm/ErrorReporting.h" #include "vm/MallocProvider.h" #include "vm/Runtime.h" struct DtoaState; namespace js { +class AutoAtomsZone; class AutoRealm; namespace jit { class JitContext; class DebugModeOSRVolatileJitFrameIter; } // namespace jit namespace gc { @@ -194,66 +195,72 @@ struct JSContext : public JS::RootingCon * places in the VM cannot know that they were called from script (e.g., * they may have been called through the JSAPI via JS_CallFunction) and thus * cannot expect there is a scripted caller. * * Realms should be entered/left in a LIFO fasion. The depth of this * enter/leave stack is maintained by enterRealmDepth_ and queried by * hasEnteredRealm. * - * To enter a compartment, code should prefer using AutoRealm over + * To enter a realm, code should prefer using AutoRealm over * manually calling cx->enterRealm/leaveRealm. */ protected: +#ifdef DEBUG js::ThreadData<unsigned> enterRealmDepth_; +#endif - inline void setRealm(JS::Realm* realm, - const js::AutoLockForExclusiveAccess* maybeLock = nullptr); + inline void setRealm(JS::Realm* realm); public: +#ifdef DEBUG bool hasEnteredRealm() const { return enterRealmDepth_ > 0; } -#ifdef DEBUG unsigned getEnterRealmDepth() const { return enterRealmDepth_; } #endif private: - // We distinguish between entering the atoms realm and all other realms. - // Entering the atoms realm requires a lock. - inline void enterNonAtomsRealm(JS::Realm* realm); - inline void enterAtomsRealm(JS::Realm* realm, - const js::AutoLockForExclusiveAccess& lock); + inline void enterRealm(JS::Realm* realm); + inline void enterAtomsZone(const js::AutoLockForExclusiveAccess& lock); + friend class js::AutoAtomsZone; friend class js::AutoRealm; public: template <typename T> inline void enterRealmOf(const T& target); inline void enterNullRealm(); - inline void leaveRealm(JS::Realm* oldRealm, - const js::AutoLockForExclusiveAccess* maybeLock = nullptr); + + inline void leaveRealm(JS::Realm* oldRealm); + inline void leaveAtomsZone(JS::Realm* oldRealm, + const js::AutoLockForExclusiveAccess& lock); void setHelperThread(js::HelperThread* helperThread); js::HelperThread* helperThread() const { return helperThread_; } bool isNurseryAllocSuppressed() const { return nurserySuppressions_; } // Threads may freely access any data in their compartment and zone. JSCompartment* compartment() const { return JS::GetCompartmentForRealm(realm_); } JS::Realm* realm() const { return realm_; } + +#ifdef DEBUG + bool inAtomsZone() const; +#endif + JS::Zone* zone() const { - MOZ_ASSERT_IF(!realm(), !zone_); + MOZ_ASSERT_IF(!realm() && zone_, inAtomsZone()); MOZ_ASSERT_IF(realm(), js::GetCompartmentZone(GetCompartmentForRealm(realm())) == zone_); return zoneRaw(); } // For use when the context's zone is being read by another thread and the // compartment and zone pointers might not be in sync. JS::Zone* zoneRaw() const { return zone_; @@ -270,18 +277,18 @@ struct JSContext : public JS::RootingCon // Current global. This is only safe to use within the scope of the // AutoRealm from which it's called. inline js::Handle<js::GlobalObject*> global() const; // Methods to access runtime data that must be protected by locks. js::AtomSet& atoms(js::AutoLockForExclusiveAccess& lock) { return runtime_->atoms(lock); } - JS::Realm* atomsRealm(js::AutoLockForExclusiveAccess& lock) { - return runtime_->atomsRealm(lock); + const JS::Zone* atomsZone(js::AutoLockForExclusiveAccess& lock) { + return runtime_->atomsZone(lock); } js::SymbolRegistry& symbolRegistry(js::AutoLockForExclusiveAccess& lock) { return runtime_->symbolRegistry(lock); } js::ScriptDataTable& scriptDataTable(js::AutoLockScriptData& lock) { return runtime_->scriptDataTable(lock); }
--- a/js/src/vm/MemoryMetrics.cpp +++ b/js/src/vm/MemoryMetrics.cpp @@ -831,17 +831,17 @@ CollectRuntimeStatsHelper(JSContext* cx, #ifdef DEBUG // Check that the in-arena measurements look ok. size_t totalArenaSize = rtStats->zTotals.gcHeapArenaAdmin + rtStats->zTotals.unusedGCThings.totalSize() + rtStats->gcHeapGCThings; MOZ_ASSERT(totalArenaSize % gc::ArenaSize == 0); #endif - for (RealmsIter realm(rt, WithAtoms); !realm.done(); realm.next()) + for (RealmsIter realm(rt); !realm.done(); realm.next()) realm->nullRealmStats(); size_t numDirtyChunks = (rtStats->gcHeapChunkTotal - rtStats->gcHeapUnusedChunks) / gc::ChunkSize; size_t perChunkAdmin = sizeof(gc::Chunk) - (sizeof(gc::Arena) * gc::ArenasPerChunk); rtStats->gcHeapChunkAdmin = numDirtyChunks * perChunkAdmin; @@ -881,28 +881,28 @@ JS::CollectRuntimeStats(JSContext* cx, R { return CollectRuntimeStatsHelper(cx, rtStats, opv, anonymize, StatsCellCallback<FineGrained>); } JS_PUBLIC_API(size_t) JS::SystemRealmCount(JSContext* cx) { size_t n = 0; - for (RealmsIter realm(cx->runtime(), WithAtoms); !realm.done(); realm.next()) { + for (RealmsIter realm(cx->runtime()); !realm.done(); realm.next()) { if (realm->isSystem()) ++n; } return n; } JS_PUBLIC_API(size_t) JS::UserRealmCount(JSContext* cx) { size_t n = 0; - for (RealmsIter realm(cx->runtime(), WithAtoms); !realm.done(); realm.next()) { + for (RealmsIter realm(cx->runtime()); !realm.done(); realm.next()) { if (!realm->isSystem()) ++n; } return n; } JS_PUBLIC_API(size_t) JS::PeakSizeOfTemporary(const JSContext* cx)
--- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -152,17 +152,16 @@ JSRuntime::JSRuntime(JSRuntime* parentRu thousandsSeparator(nullptr), decimalSeparator(nullptr), numGrouping(nullptr), #endif beingDestroyed_(false), allowContentJS_(true), atoms_(nullptr), atomsAddedWhileSweeping_(nullptr), - atomsRealm_(nullptr), staticStrings(nullptr), commonNames(nullptr), permanentAtoms(nullptr), wellKnownSymbols(nullptr), jitSupportsFloatingPoint(false), jitSupportsUnalignedAccesses(false), jitSupportsSimd(false), offthreadIonCompilationEnabled_(true), @@ -214,34 +213,18 @@ JSRuntime::init(JSContext* cx, uint32_t if (!gc.init(maxbytes, maxNurseryBytes)) return false; ScopedJSDeletePtr<Zone> atomsZone(js_new<Zone>(this)); if (!atomsZone || !atomsZone->init(true)) return false; - JS::RealmOptions options; - ScopedJSDeletePtr<Realm> atomsRealm(js_new<Realm>(atomsZone.get(), options)); - if (!atomsRealm || !atomsRealm->init(nullptr)) - return false; - - JSCompartment* atomsComp = atomsRealm->compartment(); - if (!atomsComp->realms().append(atomsRealm)) - return false; - gc.atomsZone = atomsZone.get(); - if (!atomsZone->compartments().append(atomsComp)) - return false; - - atomsRealm->setIsSystem(true); - atomsRealm->setIsAtomsRealm(); - atomsZone.forget(); - this->atomsRealm_ = atomsRealm.forget(); if (!symbolRegistry_.ref().init()) return false; if (!scriptDataTable_.ref().init()) return false; /* The garbage collector depends on everything before this point being initialized. */ @@ -328,17 +311,16 @@ JSRuntime::destroyRuntime() */ FreeScriptData(this); #if !EXPOSE_INTL_API FinishRuntimeNumberState(this); #endif gc.finish(); - atomsRealm_ = nullptr; js_delete(defaultFreeOp_.ref()); js_free(defaultLocale); js_delete(jitRuntime_.ref()); #ifdef DEBUG initialized_ = false; @@ -432,17 +414,17 @@ JSRuntime::addSizeOfIncludingThis(mozill rtSizes->wasmRuntime += wasmInstances.lock()->sizeOfExcludingThis(mallocSizeOf); } static bool HandleInterrupt(JSContext* cx, bool invokeCallback) { MOZ_ASSERT(cx->requestDepth >= 1); - MOZ_ASSERT(!cx->realm()->isAtomsRealm()); + MOZ_ASSERT(!cx->zone()->isAtomsZone()); cx->runtime()->gc.gcIfRequested(); // A worker thread may have requested an interrupt after finishing an Ion // compilation. jit::AttachFinishedCompilations(cx); // Don't call the interrupt callback if we only interrupted for GC or Ion. @@ -711,16 +693,30 @@ JSRuntime::randomHashCodeScrambler() mozilla::non_crypto::XorShift128PlusRNG JSRuntime::forkRandomKeyGenerator() { auto& rng = randomKeyGenerator(); return mozilla::non_crypto::XorShift128PlusRNG(rng.next(), rng.next()); } +js::HashNumber +JSRuntime::randomHashCode() +{ + MOZ_ASSERT(CurrentThreadCanAccessRuntime(this)); + + if (randomHashCodeGenerator_.isNothing()) { + mozilla::Array<uint64_t, 2> seed; + GenerateXorShift128PlusSeed(seed); + randomHashCodeGenerator_.emplace(seed[0], seed[1]); + } + + return HashNumber(randomHashCodeGenerator_->next()); +} + void JSRuntime::updateMallocCounter(size_t nbytes) { gc.updateMallocCounter(nbytes); } JS_FRIEND_API(void*) JSRuntime::onOutOfMemory(AllocFunction allocFunc, size_t nbytes, void* reallocPtr, JSContext* maybecx) @@ -765,17 +761,17 @@ JSRuntime::onOutOfMemoryCanGC(AllocFunct if (OnLargeAllocationFailure && bytes >= LARGE_ALLOCATION) OnLargeAllocationFailure(); return onOutOfMemory(allocFunc, bytes, reallocPtr); } bool JSRuntime::activeGCInAtomsZone() { - Zone* zone = atomsRealm_->zone(); + Zone* zone = unsafeAtomsZone(); return (zone->needsIncrementalBarrier() && !gc.isVerifyPreBarriersEnabled()) || zone->wasGCStarted(); } bool JSRuntime::createAtomsAddedWhileSweepingTable() { MOZ_ASSERT(JS::CurrentThreadIsHeapCollecting());
--- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -563,20 +563,25 @@ struct JSRuntime : public js::MallocProv return !!jitRuntime_; } private: // Used to generate random keys for hash tables. mozilla::Maybe<mozilla::non_crypto::XorShift128PlusRNG> randomKeyGenerator_; mozilla::non_crypto::XorShift128PlusRNG& randomKeyGenerator(); + // Used to generate random hash codes for symbols. + mozilla::Maybe<mozilla::non_crypto::XorShift128PlusRNG> randomHashCodeGenerator_; + public: mozilla::HashCodeScrambler randomHashCodeScrambler(); mozilla::non_crypto::XorShift128PlusRNG forkRandomKeyGenerator(); + js::HashNumber randomHashCode(); + //------------------------------------------------------------------------- // Self-hosting support //------------------------------------------------------------------------- bool hasInitializedSelfHosting() const { return selfHostingGlobal_; } @@ -694,21 +699,16 @@ struct JSRuntime : public js::MallocProv // Set of all atoms other than those in permanentAtoms and staticStrings. // Reading or writing this set requires the calling thread to use // AutoLockForExclusiveAccess. js::ExclusiveAccessLockOrGCTaskData<js::AtomSet*> atoms_; // Set of all atoms added while the main atoms table is being swept. js::ExclusiveAccessLockData<js::AtomSet*> atomsAddedWhileSweeping_; - // Realm and associated zone containing all atoms in the runtime, as - // well as runtime wide IonCode stubs. Modifying the contents of this - // zone requires the calling thread to use AutoLockForExclusiveAccess. - js::WriteOnceData<JS::Realm*> atomsRealm_; - // Set of all live symbols produced by Symbol.for(). All such symbols are // allocated in the atomsZone. Reading or writing the symbol registry // requires the calling thread to use AutoLockForExclusiveAccess. js::ExclusiveAccessLockOrGCTaskData<js::SymbolRegistry> symbolRegistry_; public: bool initializeAtoms(JSContext* cx); void finishAtoms(); @@ -729,35 +729,26 @@ struct JSRuntime : public js::MallocProv } bool createAtomsAddedWhileSweepingTable(); void destroyAtomsAddedWhileSweepingTable(); js::AtomSet* atomsAddedWhileSweeping() { return atomsAddedWhileSweeping_; } - JS::Realm* atomsRealm(js::AutoLockForExclusiveAccess& lock) { - return atomsRealm_; - } - JS::Realm* unsafeAtomsRealm() { - return atomsRealm_; + const JS::Zone* atomsZone(const js::AutoLockForExclusiveAccess& lock) const { + return gc.atomsZone; } - - // Note: once JS::Realm and JSCompartment are completely unrelated, the - // atoms realm probably won't have a compartment so we can remove this - // then. - bool isAtomsCompartment(JSCompartment* comp) { - return JS::GetRealmForCompartment(comp) == atomsRealm_; + JS::Zone* atomsZone(const js::AutoLockForExclusiveAccess& lock) { + return gc.atomsZone; } - - const JS::Zone* atomsZone(js::AutoLockForExclusiveAccess& lock) const { + JS::Zone* unsafeAtomsZone() { return gc.atomsZone; } - // The atoms realm is the only one in its zone. bool isAtomsZone(const JS::Zone* zone) const { return zone == gc.atomsZone; } bool activeGCInAtomsZone(); js::SymbolRegistry& symbolRegistry(js::AutoLockForExclusiveAccess& lock) { return symbolRegistry_.ref();
--- a/js/src/vm/Stopwatch.cpp +++ b/js/src/vm/Stopwatch.cpp @@ -189,17 +189,17 @@ PerformanceMonitoring::monotonicReadTime return 0; #endif // defined(MOZ_HAVE_RDTSC) } void PerformanceMonitoring::dispose(JSRuntime* rt) { reset(); - for (RealmsIter r(rt, SkipAtoms); !r.done(); r.next()) + for (RealmsIter r(rt); !r.done(); r.next()) r->performanceMonitoring.unlink(); } PerformanceGroupHolder::~PerformanceGroupHolder() { unlink(); }
--- a/js/src/vm/StringType-inl.h +++ b/js/src/vm/StringType-inl.h @@ -224,17 +224,17 @@ MOZ_ALWAYS_INLINE JSFlatString* JSFlatString::new_(JSContext* cx, const CharT* chars, size_t length) { MOZ_ASSERT(chars[length] == CharT(0)); if (!validateLength(cx, length)) return nullptr; JSFlatString* str; - if (cx->realm()->isAtomsRealm()) + if (cx->zone()->isAtomsZone()) str = js::Allocate<js::NormalAtom, allowGC>(cx); else str = js::Allocate<JSFlatString, allowGC>(cx, js::gc::DefaultHeap); if (!str) return nullptr; if (!str->isTenured()) { // The chars pointer is only considered to be handed over to this @@ -268,27 +268,27 @@ JSFlatString::toPropertyName(JSContext* return nullptr; return atom->asPropertyName(); } template <js::AllowGC allowGC> MOZ_ALWAYS_INLINE JSThinInlineString* JSThinInlineString::new_(JSContext* cx) { - if (cx->realm()->isAtomsRealm()) + if (cx->zone()->isAtomsZone()) return (JSThinInlineString*)(js::Allocate<js::NormalAtom, allowGC>(cx)); return js::Allocate<JSThinInlineString, allowGC>(cx, js::gc::DefaultHeap); } template <js::AllowGC allowGC> MOZ_ALWAYS_INLINE JSFatInlineString* JSFatInlineString::new_(JSContext* cx) { - if (cx->realm()->isAtomsRealm()) + if (cx->zone()->isAtomsZone()) return (JSFatInlineString*)(js::Allocate<js::FatInlineAtom, allowGC>(cx)); return js::Allocate<JSFatInlineString, allowGC>(cx, js::gc::DefaultHeap); } template<> MOZ_ALWAYS_INLINE JS::Latin1Char* JSThinInlineString::init<JS::Latin1Char>(size_t length)
--- a/js/src/vm/StringType.cpp +++ b/js/src/vm/StringType.cpp @@ -1082,17 +1082,17 @@ const StaticStrings::SmallChar StaticStr #undef R4 #undef R6 #undef R7 bool StaticStrings::init(JSContext* cx) { AutoLockForExclusiveAccess lock(cx); - AutoAtomsRealm ar(cx, lock); + AutoAtomsZone az(cx, lock); static_assert(UNIT_STATIC_LIMIT - 1 <= JSString::MAX_LATIN1_CHAR, "Unit strings must fit in Latin1Char."); using Latin1Range = mozilla::Range<const Latin1Char>; for (uint32_t i = 0; i < UNIT_STATIC_LIMIT; i++) { Latin1Char buffer[] = { Latin1Char(i), '\0' };
--- a/js/src/vm/SymbolType.cpp +++ b/js/src/vm/SymbolType.cpp @@ -17,17 +17,17 @@ using JS::Symbol; using namespace js; Symbol* Symbol::newInternal(JSContext* cx, JS::SymbolCode code, uint32_t hash, JSAtom* description, AutoLockForExclusiveAccess& lock) { - MOZ_ASSERT(cx->realm() == cx->atomsRealm(lock)); + MOZ_ASSERT(cx->zone() == cx->atomsZone(lock)); // Following js::AtomizeString, we grudgingly forgo last-ditch GC here. Symbol* p = Allocate<JS::Symbol, NoGC>(cx); if (!p) { ReportOutOfMemory(cx); return nullptr; } return new (p) Symbol(code, hash, description); @@ -43,18 +43,18 @@ Symbol::new_(JSContext* cx, JS::SymbolCo return nullptr; } // Lock to allocate. If symbol allocation becomes a bottleneck, this can // probably be replaced with an assertion that we're on the main thread. AutoLockForExclusiveAccess lock(cx); Symbol* sym; { - AutoAtomsRealm ar(cx, lock); - sym = newInternal(cx, code, cx->realm()->randomHashCode(), atom, lock); + AutoAtomsZone az(cx, lock); + sym = newInternal(cx, code, cx->runtime()->randomHashCode(), atom, lock); } if (sym) cx->markAtom(sym); return sym; } Symbol* Symbol::for_(JSContext* cx, HandleString description) @@ -69,17 +69,17 @@ Symbol::for_(JSContext* cx, HandleString SymbolRegistry::AddPtr p = registry.lookupForAdd(atom); if (p) { cx->markAtom(*p); return *p; } Symbol* sym; { - AutoAtomsRealm ar(cx, lock); + AutoAtomsZone az(cx, lock); // Rehash the hash of the atom to give the corresponding symbol a hash // that is different than the hash of the corresponding atom. HashNumber hash = mozilla::HashGeneric(atom->hash()); sym = newInternal(cx, SymbolCode::InSymbolRegistry, hash, atom, lock); if (!sym) return nullptr; // p is still valid here because we have held the lock since the
--- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -192,17 +192,17 @@ CompartmentPrivate::~CompartmentPrivate( void CompartmentPrivate::SystemIsBeingShutDown() { mWrappedJSMap->ShutdownMarker(); } RealmPrivate::RealmPrivate(JS::Realm* realm) - : scriptability(JS::GetCompartmentForRealm(realm)) + : scriptability(realm) , scope(nullptr) { } static bool TryParseLocationURICandidate(const nsACString& uristr, CompartmentPrivate::LocationHint aLocationHint, nsIURI** aURI) @@ -362,21 +362,21 @@ PrincipalImmuneToScriptPolicy(nsIPrincip return true; } } } return false; } -Scriptability::Scriptability(JSCompartment* c) : mScriptBlocks(0) +Scriptability::Scriptability(JS::Realm* realm) : mScriptBlocks(0) , mDocShellAllowsScript(true) , mScriptBlockedByPolicy(false) { - nsIPrincipal* prin = nsJSPrincipals::get(JS_GetCompartmentPrincipals(c)); + nsIPrincipal* prin = nsJSPrincipals::get(JS::GetRealmPrincipals(realm)); mImmuneToScriptPolicy = PrincipalImmuneToScriptPolicy(prin); // If we're not immune, we should have a real principal with a codebase URI. // Check the URI against the new-style domain policy. if (!mImmuneToScriptPolicy) { nsCOMPtr<nsIURI> codebase; nsresult rv = prin->GetURI(getter_AddRefs(codebase)); bool policyAllows; @@ -1072,22 +1072,21 @@ XPCJSRuntime::~XPCJSRuntime() } // If |*anonymizeID| is non-zero and this is a user compartment, the name will // be anonymized. static void GetCompartmentName(JSCompartment* c, nsCString& name, int* anonymizeID, bool replaceSlashes) { - if (js::IsAtomsRealm(JS::GetRealmForCompartment(c))) { - name.AssignLiteral("atoms"); - } else if (*anonymizeID && !js::IsSystemCompartment(c)) { + JS::Realm* realm = JS::GetRealmForCompartment(c); + if (*anonymizeID && !js::IsSystemCompartment(c)) { name.AppendPrintf("<anonymized-%d>", *anonymizeID); *anonymizeID += 1; - } else if (JSPrincipals* principals = JS_GetCompartmentPrincipals(c)) { + } else if (JSPrincipals* principals = JS::GetRealmPrincipals(realm)) { nsresult rv = nsJSPrincipals::get(principals)->GetScriptLocation(name); if (NS_FAILED(rv)) { name.AssignLiteral("(unknown)"); } // If the compartment's location (name) differs from the principal's // script location, append the compartment's location to allow // differentiation of multiple compartments owned by the same principal @@ -2158,30 +2157,33 @@ class XPCJSRuntimeStats : public JS::Run for (size_t i = 0; i != realmStatsVector.length(); ++i) delete static_cast<xpc::RealmStatsExtras*>(realmStatsVector[i].extra); for (size_t i = 0; i != zoneStatsVector.length(); ++i) delete static_cast<xpc::ZoneStatsExtras*>(zoneStatsVector[i].extra); } virtual void initExtraZoneStats(JS::Zone* zone, JS::ZoneStats* zStats) override { - // Get some global in this zone. AutoSafeJSContext cx; - Rooted<Realm*> realm(cx, js::GetAnyRealmInZone(zone)); xpc::ZoneStatsExtras* extras = new xpc::ZoneStatsExtras; extras->pathPrefix.AssignLiteral("explicit/js-non-window/zones/"); - RootedObject global(cx, JS::GetRealmGlobalOrNull(realm)); - if (global) { - RefPtr<nsGlobalWindowInner> window; - if (NS_SUCCEEDED(UNWRAP_OBJECT(Window, global, window))) { - // The global is a |window| object. Use the path prefix that - // we should have already created for it. - if (mTopWindowPaths->Get(window->WindowID(), - &extras->pathPrefix)) - extras->pathPrefix.AppendLiteral("/js-"); + + // Get some global in this zone. + Rooted<Realm*> realm(cx, js::GetAnyRealmInZone(zone)); + if (realm) { + RootedObject global(cx, JS::GetRealmGlobalOrNull(realm)); + if (global) { + RefPtr<nsGlobalWindowInner> window; + if (NS_SUCCEEDED(UNWRAP_OBJECT(Window, global, window))) { + // The global is a |window| object. Use the path prefix that + // we should have already created for it. + if (mTopWindowPaths->Get(window->WindowID(), + &extras->pathPrefix)) + extras->pathPrefix.AppendLiteral("/js-"); + } } } extras->pathPrefix += nsPrintfCString("zone(0x%p)/", (void*)zone); MOZ_ASSERT(StartsWithExplicit(extras->pathPrefix)); zStats->extra = extras;
--- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -38,17 +38,17 @@ class Exception; } typedef void (* xpcGCCallback)(JSGCStatus status); namespace xpc { class Scriptability { public: - explicit Scriptability(JSCompartment* c); + explicit Scriptability(JS::Realm* realm); bool Allowed(); bool IsImmuneToScriptPolicy(); void Block(); void Unblock(); void SetDocShellAllowsScript(bool aAllowed); static Scriptability& Get(JSObject* aScope);
--- a/media/mtransport/fuzztest/stun_parser_libfuzz.cpp +++ b/media/mtransport/fuzztest/stun_parser_libfuzz.cpp @@ -21,18 +21,17 @@ int FuzzingInitStunParser(int *argc, cha } static int RunStunParserFuzzing(const uint8_t* data, size_t size) { nr_stun_message *req = 0; UCHAR* mes = (UCHAR*)data; - nr_stun_message_create2(&req, mes, size); - - nr_stun_decode_message(req, nullptr, nullptr); - - nr_stun_message_destroy(&req); + if (!nr_stun_message_create2(&req, mes, size)) { + nr_stun_decode_message(req, nullptr, nullptr); + nr_stun_message_destroy(&req); + } return 0; } MOZ_FUZZING_INTERFACE_RAW(FuzzingInitStunParser, RunStunParserFuzzing, StunParser);
--- a/media/mtransport/nricectx.cpp +++ b/media/mtransport/nricectx.cpp @@ -1020,17 +1020,17 @@ std::vector<std::string> NrIceCtx::GetGl } RFREE(attrs); return ret; } nsresult NrIceCtx::ParseGlobalAttributes(std::vector<std::string> attrs) { std::vector<char *> attrs_in; - + attrs_in.reserve(attrs.size()); for (auto& attr : attrs) { attrs_in.push_back(const_cast<char *>(attr.c_str())); } int r = nr_ice_peer_ctx_parse_global_attributes(peer_, attrs_in.empty() ? nullptr : &attrs_in[0], attrs_in.size());
--- a/media/mtransport/nricemediastream.cpp +++ b/media/mtransport/nricemediastream.cpp @@ -224,17 +224,17 @@ NrIceMediaStream::~NrIceMediaStream() { } nsresult NrIceMediaStream::ParseAttributes(std::vector<std::string>& attributes) { if (!stream_) return NS_ERROR_FAILURE; std::vector<char *> attributes_in; - + attributes_in.reserve(attributes.size()); for (auto& attribute : attributes) { attributes_in.push_back(const_cast<char *>(attribute.c_str())); } // Still need to call nr_ice_ctx_parse_stream_attributes. int r = nr_ice_peer_ctx_parse_stream_attributes(ctx_peer_, stream_, attributes_in.empty() ?
--- a/media/mtransport/test/test_nr_socket_ice_unittest.cpp +++ b/media/mtransport/test/test_nr_socket_ice_unittest.cpp @@ -183,17 +183,17 @@ public: } RFREE(attrs); return ret; } void ParseGlobalAttributes(std::vector<std::string> attrs) { std::vector<char *> attrs_in; - + attrs_in.reserve(attrs.size()); for (auto& attr : attrs) { attrs_in.push_back(const_cast<char *>(attr.c_str())); } int r = nr_ice_peer_ctx_parse_global_attributes(peer_ctx_, attrs_in.empty() ? nullptr : &attrs_in[0], attrs_in.size()); @@ -203,16 +203,17 @@ public: void SetControlling(bool controlling) { peer_ctx_->controlling = controlling ? 1 : 0; } void SetRemoteAttributes(std::vector<std::string> attributes) { int r; std::vector<char*> attrs; + attrs.reserve(attributes.size()); for (auto& attr: attributes) { attrs.push_back(const_cast<char*>(attr.c_str())); } if (!attrs.empty()) { r = nr_ice_peer_ctx_parse_stream_attributes(peer_ctx_, ice_media_stream_, &attrs[0], attrs.size()); ASSERT_EQ(0, r); }
--- a/media/mtransport/third_party/nICEr/src/stun/stun_codec.c +++ b/media/mtransport/third_party/nICEr/src/stun/stun_codec.c @@ -1410,19 +1410,16 @@ nr_stun_decode_message(nr_stun_message * int r,_status; int offset; int size; int padding_bytes; nr_stun_message_attribute *attr; nr_stun_attr_info *attr_info; Data *password; - if (!msg) - ABORT(R_BAD_ARGS); - r_log(NR_LOG_STUN, LOG_DEBUG, "Parsing STUN message of %d bytes", msg->length); if (!TAILQ_EMPTY(&msg->attributes)) ABORT(R_BAD_ARGS); if (sizeof(nr_stun_message_header) > msg->length) { r_log(NR_LOG_STUN, LOG_WARNING, "Message too small"); ABORT(R_FAILED);
--- a/media/webrtc/signaling/gtest/jsep_session_unittest.cpp +++ b/media/webrtc/signaling/gtest/jsep_session_unittest.cpp @@ -164,16 +164,17 @@ protected: } } } std::vector<RefPtr<JsepTransceiver>> DeepCopy(const std::vector<RefPtr<JsepTransceiver>>& transceivers) { std::vector<RefPtr<JsepTransceiver>> copy; + copy.reserve(transceivers.size()); for (const RefPtr<JsepTransceiver>& transceiver : transceivers) { copy.push_back(new JsepTransceiver(*transceiver)); } return copy; } std::string CreateOffer(const Maybe<JsepOfferOptions>& options = Nothing())
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp @@ -3196,17 +3196,17 @@ PeerConnectionImpl::IceGatheringStateCha RefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver); if (!pco) { return; } WrappableJSErrorResult rv; mThread->Dispatch(WrapRunnable(pco, &PeerConnectionObserver::OnStateChange, PCObserverStateType::IceGatheringState, - rv, static_cast<JSCompartment*>(nullptr)), + rv, static_cast<JS::Realm*>(nullptr)), NS_DISPATCH_NORMAL); if (mIceGatheringState == PCImplIceGatheringState::Complete) { SendLocalIceCandidateToContent(0, "", ""); } } void
--- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -861,16 +861,17 @@ pref("gfx.webrender.enabled", true); pref("gfx.webrender.enabled", false); #endif #endif #ifdef XP_WIN pref("gfx.webrender.force-angle", true); pref("gfx.webrender.dcomp-win.enabled", true); pref("gfx.webrender.program-binary", true); +pref("gfx.webrender.program-binary-disk", true); #endif #ifdef XP_MACOSX pref("gfx.compositor.glcontext.opaque", false); #endif pref("gfx.webrender.highlight-painted-layers", false); pref("gfx.webrender.async-scene-build", true);
--- a/netwerk/base/nsIProtocolHandler.idl +++ b/netwerk/base/nsIProtocolHandler.idl @@ -297,20 +297,23 @@ interface nsIProtocolHandler : nsISuppor /** * Channels for this protocol don't need to spin the event loop to handle * Open() and reads on the resulting stream. */ const unsigned long URI_SYNC_LOAD_IS_OK = (1<<17); /** - * URI is secure to load in an https page and should not be blocked - * by nsMixedContentBlocker + * All the origins whose URI has this scheme are considered potentially + * trustworthy. + * Per the SecureContext spec, https: and wss: should be considered + * a priori secure, and implementations may consider other, + * implementation-specific URI schemes as secure. */ - const unsigned long URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT = (1<<18); + const unsigned long URI_IS_POTENTIALLY_TRUSTWORTHY = (1<<18); /** * This URI may be fetched and the contents are visible to anyone. This is * semantically equivalent to the resource being served with all-access CORS * headers. */ const unsigned long URI_FETCHABLE_BY_ANYONE = (1 << 19);
--- a/netwerk/protocol/about/nsAboutProtocolHandler.cpp +++ b/netwerk/protocol/about/nsAboutProtocolHandler.cpp @@ -88,17 +88,17 @@ nsAboutProtocolHandler::GetFlagsForURI(n uint32_t aboutModuleFlags = 0; rv = aboutMod->GetURIFlags(aURI, &aboutModuleFlags); // This should never happen, so pass back the error: NS_ENSURE_SUCCESS(rv, rv); // Secure (https) pages can load safe about pages without becoming // mixed content. if (aboutModuleFlags & nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT) { - *aFlags |= URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT; + *aFlags |= URI_IS_POTENTIALLY_TRUSTWORTHY; // about: pages can only be loaded by unprivileged principals // if they are marked as LINKABLE if (aboutModuleFlags & nsIAboutModule::MAKE_LINKABLE) { // Replace URI_DANGEROUS_TO_LOAD with URI_LOADABLE_BY_ANYONE. *aFlags &= ~URI_DANGEROUS_TO_LOAD; *aFlags |= URI_LOADABLE_BY_ANYONE; } } @@ -300,17 +300,17 @@ nsSafeAboutProtocolHandler::GetDefaultPo { *result = -1; // no port for moz-safe-about: URLs return NS_OK; } NS_IMETHODIMP nsSafeAboutProtocolHandler::GetProtocolFlags(uint32_t *result) { - *result = URI_NORELATIVE | URI_NOAUTH | URI_LOADABLE_BY_ANYONE | URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT; + *result = URI_NORELATIVE | URI_NOAUTH | URI_LOADABLE_BY_ANYONE | URI_IS_POTENTIALLY_TRUSTWORTHY; return NS_OK; } NS_IMETHODIMP nsSafeAboutProtocolHandler::NewURI(const nsACString &aSpec, const char *aCharset, // ignore charset info nsIURI *aBaseURI, nsIURI **result)
--- a/netwerk/protocol/file/nsFileProtocolHandler.cpp +++ b/netwerk/protocol/file/nsFileProtocolHandler.cpp @@ -148,17 +148,18 @@ nsFileProtocolHandler::GetDefaultPort(in { *result = -1; // no port for file: URLs return NS_OK; } NS_IMETHODIMP nsFileProtocolHandler::GetProtocolFlags(uint32_t *result) { - *result = URI_NOAUTH | URI_IS_LOCAL_FILE | URI_IS_LOCAL_RESOURCE; + *result = URI_NOAUTH | URI_IS_LOCAL_FILE | + URI_IS_LOCAL_RESOURCE | URI_IS_POTENTIALLY_TRUSTWORTHY; return NS_OK; } NS_IMETHODIMP nsFileProtocolHandler::NewURI(const nsACString &spec, const char *charset, nsIURI *aBaseURI, nsIURI **result)
--- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -2708,17 +2708,17 @@ nsHttpsHandler::GetDefaultPort(int32_t * { *aPort = NS_HTTPS_DEFAULT_PORT; return NS_OK; } NS_IMETHODIMP nsHttpsHandler::GetProtocolFlags(uint32_t *aProtocolFlags) { - *aProtocolFlags = NS_HTTP_PROTOCOL_FLAGS | URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT; + *aProtocolFlags = NS_HTTP_PROTOCOL_FLAGS | URI_IS_POTENTIALLY_TRUSTWORTHY; return NS_OK; } NS_IMETHODIMP nsHttpsHandler::NewURI(const nsACString &aSpec, const char *aOriginCharset, nsIURI *aBaseURI, nsIURI **_retval)
--- a/netwerk/protocol/res/ExtensionProtocolHandler.cpp +++ b/netwerk/protocol/res/ExtensionProtocolHandler.cpp @@ -370,17 +370,19 @@ ExtensionProtocolHandler::GetFlagsForURI // subset are web-accessible (and cross-origin fetchable). Check that whitelist. bool loadableByAnyone = false; URLInfo url(aURI); if (auto* policy = EPS().GetByURL(url)) { loadableByAnyone = policy->IsPathWebAccessible(url.FilePath()); } - *aFlags = URI_STD | URI_IS_LOCAL_RESOURCE | (loadableByAnyone ? (URI_LOADABLE_BY_ANYONE | URI_FETCHABLE_BY_ANYONE) : URI_DANGEROUS_TO_LOAD); + *aFlags = URI_STD | URI_IS_LOCAL_RESOURCE | URI_IS_POTENTIALLY_TRUSTWORTHY | + (loadableByAnyone ? (URI_LOADABLE_BY_ANYONE | + URI_FETCHABLE_BY_ANYONE) : URI_DANGEROUS_TO_LOAD); return NS_OK; } bool ExtensionProtocolHandler::ResolveSpecialCases(const nsACString& aHost, const nsACString& aPath, const nsACString& aPathname, nsACString& aResult)
--- a/netwerk/protocol/res/nsResProtocolHandler.h +++ b/netwerk/protocol/res/nsResProtocolHandler.h @@ -22,17 +22,20 @@ class nsResProtocolHandler final : publi { public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIRESPROTOCOLHANDLER NS_FORWARD_NSIPROTOCOLHANDLER(mozilla::net::SubstitutingProtocolHandler::) nsResProtocolHandler() - : mozilla::net::SubstitutingProtocolHandler("resource", URI_STD | URI_IS_UI_RESOURCE | URI_IS_LOCAL_RESOURCE, + : mozilla::net::SubstitutingProtocolHandler("resource", URI_STD | + URI_IS_UI_RESOURCE | + URI_IS_LOCAL_RESOURCE | + URI_IS_POTENTIALLY_TRUSTWORTHY, /* aEnforceFileOrJar = */ false) {} MOZ_MUST_USE nsresult Init(); NS_IMETHOD SetSubstitution(const nsACString& aRoot, nsIURI* aBaseURI) override; NS_IMETHOD SetSubstitutionWithFlags(const nsACString& aRoot, nsIURI* aBaseURI, uint32_t aFlags) override;
--- a/netwerk/protocol/websocket/BaseWebSocketChannel.cpp +++ b/netwerk/protocol/websocket/BaseWebSocketChannel.cpp @@ -285,16 +285,19 @@ BaseWebSocketChannel::GetDefaultPort(int NS_IMETHODIMP BaseWebSocketChannel::GetProtocolFlags(uint32_t *aProtocolFlags) { LOG(("BaseWebSocketChannel::GetProtocolFlags() %p\n", this)); *aProtocolFlags = URI_NORELATIVE | URI_NON_PERSISTABLE | ALLOWS_PROXY | ALLOWS_PROXY_HTTP | URI_DOES_NOT_RETURN_DATA | URI_DANGEROUS_TO_LOAD; + if (mEncrypted) { + *aProtocolFlags |= URI_IS_POTENTIALLY_TRUSTWORTHY; + } return NS_OK; } NS_IMETHODIMP BaseWebSocketChannel::NewURI(const nsACString & aSpec, const char *aOriginCharset, nsIURI *aBaseURI, nsIURI **_retval) { LOG(("BaseWebSocketChannel::NewURI() %p\n", this));
--- a/testing/config/tooltool-manifests/linux64/ccov.manifest +++ b/testing/config/tooltool-manifests/linux64/ccov.manifest @@ -1,9 +1,9 @@ [ { "size": 2161039, "digest": "e441dcd0caa5c4e0ebad9545920a576a02dcaf3f14438f2b8eafc8b15d40c9de58bd782f4ef2b668ac4098d1b7461d12497f89f4a26f870fd0bbeb83d0a4a74c", "algorithm": "sha512", - "filename": "grcov-linux-standalone-x86_64.tar.bz2", + "filename": "grcov-linux-x86_64.tar.bz2", "unpack": false } ]
new file mode 100644 --- /dev/null +++ b/testing/config/tooltool-manifests/macosx64/ccov.manifest @@ -0,0 +1,9 @@ +[ + { + "size": 874769, + "visibility": "public", + "digest": "edab0125906d6258f752cbebb2129e6f0719179c23eb910340f71efb2623f82b55edd525063d9ab39d338b9fa438316ca689de02cee4f284bc459df14b400bbe", + "algorithm": "sha512", + "filename": "grcov-osx-x86_64.tar.bz2" + } +]
--- a/testing/mozharness/mozharness/mozilla/testing/codecoverage.py +++ b/testing/mozharness/mozharness/mozilla/testing/codecoverage.py @@ -15,21 +15,16 @@ import uuid import mozinfo from mozharness.base.script import ( PreScriptAction, PostScriptAction, ) from mozharness.mozilla.testing.per_test_base import SingleTestMixin -_here = os.path.abspath(os.path.dirname(__file__)) -_tooltool_path = os.path.normpath(os.path.join(_here, '..', '..', '..', - 'external_tools', - 'tooltool.py')) - code_coverage_config_options = [ [["--code-coverage"], {"action": "store_true", "dest": "code_coverage", "default": False, "help": "Whether gcov c++ code coverage should be run." }], [["--per-test-coverage"], @@ -98,50 +93,57 @@ class CodeCoverageMixin(SingleTestMixin) except (AttributeError, KeyError, TypeError): return False @PostScriptAction('download-and-extract') def setup_coverage_tools(self, action, success=None): if not self.code_coverage_enabled: return - if mozinfo.os == 'linux': + if mozinfo.os == 'linux' or mozinfo.os == 'mac': self.prefix = '/builds/worker/workspace/build/src/' strip_count = self.prefix.count('/') elif mozinfo.os == 'win': self.prefix = 'z:/build/build/src/' # Add 1 as on Windows the path where the compiler tries to write the # gcda files has an additional 'obj-firefox' component. strip_count = self.prefix.count('/') + 1 + else: + raise Exception('Unexpected OS: {}'.format(mozinfo.os)) os.environ['GCOV_PREFIX_STRIP'] = str(strip_count) # Install grcov on the test machine # Get the path to the build machines gcno files. self.url_to_gcno = self.query_build_dir_url('target.code-coverage-gcno.zip') self.url_to_chrome_map = self.query_build_dir_url('chrome-map.json') dirs = self.query_abs_dirs() # Create the grcov directory, get the tooltool manifest, and finally # download and unpack the grcov binary. self.grcov_dir = tempfile.mkdtemp() if mozinfo.os == 'linux': platform = 'linux64' - tar_file = 'grcov-linux-standalone-x86_64.tar.bz2' + tar_file = 'grcov-linux-x86_64.tar.bz2' elif mozinfo.os == 'win': platform = 'win32' tar_file = 'grcov-win-i686.tar.bz2' + elif mozinfo.os == 'mac': + platform = 'macosx64' + tar_file = 'grcov-osx-x86_64.tar.bz2' manifest = os.path.join(dirs.get('abs_test_install_dir', os.path.join(dirs['abs_work_dir'], 'tests')), \ 'config/tooltool-manifests/%s/ccov.manifest' % platform) - cmd = [sys.executable, _tooltool_path, '--url', 'https://tooltool.mozilla-releng.net/', 'fetch', \ - '-m', manifest, '-o', '-c', '/builds/worker/tooltool-cache'] - self.run_command(cmd, cwd=self.grcov_dir) + self.tooltool_fetch( + manifest=manifest, + output_dir=self.grcov_dir, + cache=self.config.get('tooltool_cache') + ) with tarfile.open(os.path.join(self.grcov_dir, tar_file)) as tar: tar.extractall(self.grcov_dir) # Download the gcno archive from the build machine. self.download_file(self.url_to_gcno, parent_dir=self.grcov_dir) # Download the chrome-map.json file from the build machine. @@ -274,17 +276,17 @@ class CodeCoverageMixin(SingleTestMixin) ] if 'coveralls' in output_format: grcov_command += ['--token', 'UNUSED', '--commit-sha', 'UNUSED'] if merge: grcov_command += [jsvm_output_file] - if mozinfo.os == 'win': + if mozinfo.os == 'win' or mozinfo.os == 'mac': grcov_command += ['--llvm'] if filter_covered: grcov_command += ['--filter', 'covered'] # 'grcov_output' will be a tuple, the first variable is the path to the lcov output, # the other is the path to the standard error output. tmp_output_file, _ = self.get_output_from_command(
--- a/testing/web-platform/meta/2dcontext/drawing-paths-to-the-canvas/drawFocusIfNeeded_001.html.ini +++ b/testing/web-platform/meta/2dcontext/drawing-paths-to-the-canvas/drawFocusIfNeeded_001.html.ini @@ -1,9 +1,6 @@ [drawFocusIfNeeded_001.html] disabled: if os == "win": https://bugzilla.mozilla.org/show_bug.cgi?id=1092458 [drawFocusIfNeeded draws a focus ring.] expected: - if not debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): FAIL - if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL if not debug and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL -
--- a/testing/web-platform/meta/2dcontext/drawing-paths-to-the-canvas/drawFocusIfNeeded_005.html.ini +++ b/testing/web-platform/meta/2dcontext/drawing-paths-to-the-canvas/drawFocusIfNeeded_005.html.ini @@ -1,9 +1,6 @@ [drawFocusIfNeeded_005.html] disabled: if os == "win": https://bugzilla.mozilla.org/show_bug.cgi?id=1092458 [drawFocusIfNeeded does draw a focus ring if the element is in focus and the user activated a particular focus ring.] expected: - if not debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): FAIL - if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL if not debug and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL -
--- a/testing/web-platform/meta/content-security-policy/generic/generic-0_10.html.ini +++ b/testing/web-platform/meta/content-security-policy/generic/generic-0_10.html.ini @@ -1,9 +1,4 @@ [generic-0_10.html] disabled: if not debug and (os == "win"): https://bugzilla.mozilla.org/show_bug.cgi?id=1131091 - expected: - if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): TIMEOUT - [Violation report status OK.] - expected: - if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): TIMEOUT
--- a/testing/web-platform/meta/content-security-policy/media-src/media-src-7_1.html.ini +++ b/testing/web-platform/meta/content-security-policy/media-src/media-src-7_1.html.ini @@ -1,12 +1,3 @@ [media-src-7_1.html] disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1124091 - [In-policy async video src] - expected: - if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL - if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL - [In-policy async video source element] - expected: - if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL - if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL -
deleted file mode 100644 --- a/testing/web-platform/meta/content-security-policy/media-src/media-src-redir-bug.sub.html.ini +++ /dev/null @@ -1,19 +0,0 @@ -[media-src-redir-bug.sub.html] - expected: - if (os == "win") and (version == "5.1.2600"): TIMEOUT - [In-policy async video src] - expected: - if (os == "win") and (version == "5.1.2600"): FAIL - - [in-policy async video src w/redir] - expected: - if (os == "win") and (version == "5.1.2600"): FAIL - - [In-policy async video source element] - expected: - if (os == "win") and (version == "5.1.2600"): FAIL - - [In-policy async video source element w/redir] - expected: - if (os == "win") and (version == "5.1.2600"): NOTRUN -
--- a/toolkit/components/places/tests/unit/xpcshell.ini +++ b/toolkit/components/places/tests/unit/xpcshell.ini @@ -42,17 +42,16 @@ skip-if = os == "linux" [test_1085291.js] [test_1105208.js] [test_1105866.js] [test_adaptive.js] [test_adaptive_bug527311.js] [test_annotations.js] [test_asyncExecuteLegacyQueries.js] [test_async_transactions.js] -skip-if = (os == "win" && os_version == "5.1") # Bug 1158887 [test_bookmarks_json.js] [test_bookmarks_json_corrupt.js] [test_bookmarks_html.js] [test_bookmarks_html_corrupt.js] [test_bookmarks_html_escape_entities.js] [test_bookmarks_html_import_tags.js] [test_bookmarks_html_singleframe.js] [test_bookmarks_restore_notification.js]
--- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -13779,10 +13779,30 @@ "alert_emails": ["mnakano@mozilla.com"], "bug_numbers": [1452538,1449564], "expires_in_version": "65", "kind": "linear", "high": 50, "n_buckets": 20, "releaseChannelCollection": "opt-out", "description": "Number of HTML editors whose inline table editing UI is actually used by users." + }, + "ACTIVE_DOCGROUPS_PER_TABGROUP": { + "record_in_processes": ["content"], + "alert_emails": ["farre@mozilla.com"], + "bug_numbers": [1441972], + "expires_in_version": "67", + "kind": "exponential", + "high": 50, + "n_buckets": 20, + "description": "Number of active doc groups per tab group. Collected at the point when the top level document of the tab group is unloaded." + }, + "TOTAL_DOCGROUPS_PER_TABGROUP": { + "record_in_processes": ["content"], + "alert_emails": ["farre@mozilla.com"], + "bug_numbers": [1441972], + "expires_in_version": "67", + "kind": "exponential", + "high": 50, + "n_buckets": 20, + "description": "Total number of doc groups per tab group, including docgroups fully in bfcache. Collected at the point when the top level document of the tab group is unloaded." } }
--- a/toolkit/components/terminator/nsTerminator.cpp +++ b/toolkit/components/terminator/nsTerminator.cpp @@ -418,16 +418,35 @@ nsTerminator::StartWatchdog() // AsyncShutdown. if (crashAfterMS > INT32_MAX - ADDITIONAL_WAIT_BEFORE_CRASH_MS) { // Defend against overflow crashAfterMS = INT32_MAX; } else { crashAfterMS += ADDITIONAL_WAIT_BEFORE_CRASH_MS; } +# ifdef MOZ_VALGRIND + // If we're running on Valgrind, we'll be making forward progress at a + // rate of somewhere between 1/25th and 1/50th of normal. This can cause + // timeouts frequently enough to be a problem for the Valgrind runs on + // automation: see bug 1296819. As an attempt to avoid the worst of this, + // scale up the presented timeout by a factor of three. For a + // non-Valgrind-enabled build, or for an enabled build which isn't running + // on Valgrind, the timeout is unchanged. + if (RUNNING_ON_VALGRIND) { + const int32_t scaleUp = 3; + if (crashAfterMS >= (INT32_MAX / scaleUp) - 1) { + // Defend against overflow + crashAfterMS = INT32_MAX; + } else { + crashAfterMS *= scaleUp; + } + } +# endif + UniquePtr<Options> options(new Options()); const PRIntervalTime ticksDuration = PR_MillisecondsToInterval(1000); options->crashAfterTicks = crashAfterMS / ticksDuration; // Handle systems where ticksDuration is greater than crashAfterMS. if (options->crashAfterTicks == 0) { options->crashAfterTicks = crashAfterMS / 1000; }
--- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -3760,21 +3760,21 @@ namespace mozilla { static void SetShutdownChecks() { // Set default first. On debug builds we crash. On nightly and local // builds we record. Nightlies will then send the info via telemetry, // but it is usefull to have the data in about:telemetry in local builds // too. #ifdef DEBUG -#if defined(MOZ_CODE_COVERAGE) && defined(XP_WIN) +#if defined(MOZ_CODE_COVERAGE) gShutdownChecks = SCM_NOTHING; #else gShutdownChecks = SCM_CRASH; -#endif // MOZ_CODE_COVERAGE && XP_WIN +#endif // MOZ_CODE_COVERAGE #else const char* releaseChannel = NS_STRINGIFY(MOZ_UPDATE_CHANNEL); if (strcmp(releaseChannel, "nightly") == 0 || strcmp(releaseChannel, "default") == 0) { gShutdownChecks = SCM_RECORD; } else { gShutdownChecks = SCM_NOTHING; }
--- a/tools/code-coverage/CodeCoverageHandler.h +++ b/tools/code-coverage/CodeCoverageHandler.h @@ -18,17 +18,17 @@ public: static CodeCoverageHandler* Get(); CrossProcessMutex* GetMutex(); CrossProcessMutexHandle GetMutexHandle(int aProcId); static void DumpCounters(int); static void ResetCounters(int); private: CodeCoverageHandler(); - CodeCoverageHandler(const CrossProcessMutexHandle& aHandle); + explicit CodeCoverageHandler(const CrossProcessMutexHandle& aHandle); static StaticAutoPtr<CodeCoverageHandler> instance; CrossProcessMutex mGcovLock; DISALLOW_COPY_AND_ASSIGN(CodeCoverageHandler); void SetSignalHandlers(); };
--- a/tools/profiler/core/platform.cpp +++ b/tools/profiler/core/platform.cpp @@ -106,19 +106,20 @@ // Android builds use the ARM Exception Handling ABI to unwind. #if defined(GP_PLAT_arm_linux) || defined(GP_PLAT_arm_android) # define HAVE_NATIVE_UNWIND # define USE_EHABI_STACKWALK # include "EHABIStackWalk.h" #endif // Linux builds use LUL, which uses DWARF info to unwind stacks. -#if defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_x86_linux) || \ - defined(GP_PLAT_mips64_linux) || defined(GP_PLAT_arm64_linux) || \ - defined(GP_PLAT_arm64_android) +#if defined(GP_PLAT_amd64_linux) || \ + defined(GP_PLAT_x86_linux) || defined(GP_PLAT_x86_android) || \ + defined(GP_PLAT_mips64_linux) || \ + defined(GP_PLAT_arm64_linux) || defined(GP_PLAT_arm64_android) # define HAVE_NATIVE_UNWIND # define USE_LUL_STACKWALK # include "lul/LulMain.h" # include "lul/platform-linux-lul.h" // On linux we use LUL for periodic samples and synchronous samples, but we use // FramePointerStackWalk for backtrace samples when MOZ_PROFILING is enabled. // (See the comment at the top of the file for a definition of
--- a/widget/MouseEvents.h +++ b/widget/MouseEvents.h @@ -8,30 +8,16 @@ #include <stdint.h> #include "mozilla/BasicEvents.h" #include "mozilla/MathAlgorithms.h" #include "mozilla/dom/DataTransfer.h" #include "nsCOMPtr.h" -/****************************************************************************** - * nsDragDropEventStatus - ******************************************************************************/ - -enum nsDragDropEventStatus -{ - // The event is a enter - nsDragDropEventStatus_eDragEntered, - // The event is exit - nsDragDropEventStatus_eDragExited, - // The event is drop - nsDragDropEventStatus_eDrop -}; - namespace mozilla { namespace dom { class PBrowserParent; class PBrowserChild; } // namespace dom class WidgetPointerEvent;
--- a/xpcom/ds/IncrementalTokenizer.h +++ b/xpcom/ds/IncrementalTokenizer.h @@ -11,17 +11,17 @@ #include "nsError.h" #include <functional> class nsIInputStream; namespace mozilla { -class IncrementalTokenizer : public TokenizerBase +class IncrementalTokenizer : public TokenizerBase<char> { public: /** * The consumer callback. The function is called for every single token * as found in the input. Failure result returned by this callback stops * the tokenization immediately and bubbles to result of Feed/FinishInput. * * Fragment()s of consumed tokens are ensured to remain valid until next call to
--- a/xpcom/ds/Tokenizer.cpp +++ b/xpcom/ds/Tokenizer.cpp @@ -6,317 +6,353 @@ #include "Tokenizer.h" #include "nsUnicharUtils.h" #include <algorithm> namespace mozilla { -static const char sWhitespaces[] = " \t"; +template<> +char const TokenizerBase<char>::sWhitespaces[] = { ' ', '\t', 0 }; +template<> +char16_t const TokenizerBase<char16_t>::sWhitespaces[3] = { ' ', '\t', 0 }; -Tokenizer::Tokenizer(const nsACString& aSource, - const char* aWhitespaces, - const char* aAdditionalWordChars) - : TokenizerBase(aWhitespaces, aAdditionalWordChars) +template<typename TChar> +static bool +contains(TChar const* const list, TChar const needle) { - mInputFinished = true; - aSource.BeginReading(mCursor); - mRecord = mRollback = mCursor; - aSource.EndReading(mEnd); + for (TChar const *c = list; *c; ++c) { + if (needle == *c) { + return true; + } + } + return false; } -Tokenizer::Tokenizer(const char* aSource, - const char* aWhitespaces, - const char* aAdditionalWordChars) - : Tokenizer(nsDependentCString(aSource), aWhitespaces, aAdditionalWordChars) +template<typename TChar> +TTokenizer<TChar>::TTokenizer(const typename base::TAString& aSource, + const TChar* aWhitespaces, + const TChar* aAdditionalWordChars) + : TokenizerBase<TChar>(aWhitespaces, aAdditionalWordChars) +{ + base::mInputFinished = true; + aSource.BeginReading(base::mCursor); + mRecord = mRollback = base::mCursor; + aSource.EndReading(base::mEnd); +} + +template<typename TChar> +TTokenizer<TChar>::TTokenizer(const TChar* aSource, + const TChar* aWhitespaces, + const TChar* aAdditionalWordChars) + : TTokenizer(typename base::TDependentString(aSource), aWhitespaces, aAdditionalWordChars) { } +template<typename TChar> bool -Tokenizer::Next(Token& aToken) +TTokenizer<TChar>::Next(typename base::Token& aToken) { - if (!HasInput()) { - mHasFailed = true; + if (!base::HasInput()) { + base::mHasFailed = true; return false; } - mRollback = mCursor; - mCursor = Parse(aToken); + mRollback = base::mCursor; + base::mCursor = base::Parse(aToken); - AssignFragment(aToken, mRollback, mCursor); + base::AssignFragment(aToken, mRollback, base::mCursor); - mPastEof = aToken.Type() == TOKEN_EOF; - mHasFailed = false; + base::mPastEof = aToken.Type() == base::TOKEN_EOF; + base::mHasFailed = false; return true; } +template<typename TChar> bool -Tokenizer::Check(const TokenType aTokenType, Token& aResult) +TTokenizer<TChar>::Check(const typename base::TokenType aTokenType, typename base::Token& aResult) { - if (!HasInput()) { - mHasFailed = true; + if (!base::HasInput()) { + base::mHasFailed = true; return false; } - nsACString::const_char_iterator next = Parse(aResult); + typename base::TAString::const_char_iterator next = base::Parse(aResult); if (aTokenType != aResult.Type()) { - mHasFailed = true; + base::mHasFailed = true; return false; } - mRollback = mCursor; - mCursor = next; + mRollback = base::mCursor; + base::mCursor = next; - AssignFragment(aResult, mRollback, mCursor); + base::AssignFragment(aResult, mRollback, base::mCursor); - mPastEof = aResult.Type() == TOKEN_EOF; - mHasFailed = false; + base::mPastEof = aResult.Type() == base::TOKEN_EOF; + base::mHasFailed = false; return true; } +template<typename TChar> bool -Tokenizer::Check(const Token& aToken) +TTokenizer<TChar>::Check(const typename base::Token& aToken) { - if (!HasInput()) { - mHasFailed = true; + if (!base::HasInput()) { + base::mHasFailed = true; return false; } - Token parsed; - nsACString::const_char_iterator next = Parse(parsed); + typename base::Token parsed; + typename base::TAString::const_char_iterator next = base::Parse(parsed); if (!aToken.Equals(parsed)) { - mHasFailed = true; + base::mHasFailed = true; return false; } - mRollback = mCursor; - mCursor = next; - mPastEof = parsed.Type() == TOKEN_EOF; - mHasFailed = false; + mRollback = base::mCursor; + base::mCursor = next; + base::mPastEof = parsed.Type() == base::TOKEN_EOF; + base::mHasFailed = false; return true; } +template<typename TChar> void -Tokenizer::SkipWhites(WhiteSkipping aIncludeNewLines) +TTokenizer<TChar>::SkipWhites(WhiteSkipping aIncludeNewLines) { if (!CheckWhite() && (aIncludeNewLines == DONT_INCLUDE_NEW_LINE || !CheckEOL())) { return; } - nsACString::const_char_iterator rollback = mRollback; + typename base::TAString::const_char_iterator rollback = mRollback; while (CheckWhite() || (aIncludeNewLines == INCLUDE_NEW_LINE && CheckEOL())) { } - mHasFailed = false; + base::mHasFailed = false; mRollback = rollback; } +template<typename TChar> void -Tokenizer::SkipUntil(Token const& aToken) +TTokenizer<TChar>::SkipUntil(typename base::Token const& aToken) { - nsACString::const_char_iterator rollback = mCursor; - const Token eof = Token::EndOfFile(); + typename base::TAString::const_char_iterator rollback = base::mCursor; + const typename base::Token eof = base::Token::EndOfFile(); - Token t; + typename base::Token t; while (Next(t)) { if (aToken.Equals(t) || eof.Equals(t)) { Rollback(); break; } } mRollback = rollback; } +template<typename TChar> bool -Tokenizer::CheckChar(bool (*aClassifier)(const char aChar)) +TTokenizer<TChar>::CheckChar(bool (*aClassifier)(const TChar aChar)) { if (!aClassifier) { MOZ_ASSERT(false); return false; } - if (!HasInput() || mCursor == mEnd) { - mHasFailed = true; + if (!base::HasInput() || base::mCursor == base::mEnd) { + base::mHasFailed = true; return false; } - if (!aClassifier(*mCursor)) { - mHasFailed = true; + if (!aClassifier(*base::mCursor)) { + base::mHasFailed = true; return false; } - mRollback = mCursor; - ++mCursor; - mHasFailed = false; + mRollback = base::mCursor; + ++base::mCursor; + base::mHasFailed = false; return true; } +template<typename TChar> bool -Tokenizer::ReadChar(char* aValue) +TTokenizer<TChar>::ReadChar(TChar* aValue) { MOZ_RELEASE_ASSERT(aValue); - Token t; - if (!Check(TOKEN_CHAR, t)) { + typename base::Token t; + if (!Check(base::TOKEN_CHAR, t)) { return false; } *aValue = t.AsChar(); return true; } +template<typename TChar> bool -Tokenizer::ReadChar(bool (*aClassifier)(const char aChar), char* aValue) +TTokenizer<TChar>::ReadChar(bool (*aClassifier)(const TChar aChar), TChar* aValue) { MOZ_RELEASE_ASSERT(aValue); if (!CheckChar(aClassifier)) { return false; } *aValue = *mRollback; return true; } +template<typename TChar> bool -Tokenizer::ReadWord(nsACString& aValue) +TTokenizer<TChar>::ReadWord(typename base::TAString& aValue) { - Token t; - if (!Check(TOKEN_WORD, t)) { + typename base::Token t; + if (!Check(base::TOKEN_WORD, t)) { return false; } aValue.Assign(t.AsString()); return true; } +template<typename TChar> bool -Tokenizer::ReadWord(nsDependentCSubstring& aValue) +TTokenizer<TChar>::ReadWord(typename base::TDependentSubstring& aValue) { - Token t; - if (!Check(TOKEN_WORD, t)) { + typename base::Token t; + if (!Check(base::TOKEN_WORD, t)) { return false; } aValue.Rebind(t.AsString().BeginReading(), t.AsString().Length()); return true; } +template<typename TChar> bool -Tokenizer::ReadUntil(Token const& aToken, nsACString& aResult, ClaimInclusion aInclude) +TTokenizer<TChar>::ReadUntil(typename base::Token const& aToken, typename base::TAString& aResult, ClaimInclusion aInclude) { - nsDependentCSubstring substring; + typename base::TDependentSubstring substring; bool rv = ReadUntil(aToken, substring, aInclude); aResult.Assign(substring); return rv; } +template<typename TChar> bool -Tokenizer::ReadUntil(Token const& aToken, nsDependentCSubstring& aResult, ClaimInclusion aInclude) +TTokenizer<TChar>::ReadUntil(typename base::Token const& aToken, typename base::TDependentSubstring& aResult, ClaimInclusion aInclude) { - nsACString::const_char_iterator record = mRecord; + typename base::TAString::const_char_iterator record = mRecord; Record(); - nsACString::const_char_iterator rollback = mRollback = mCursor; + typename base::TAString::const_char_iterator rollback = mRollback = base::mCursor; bool found = false; - Token t; + typename base::Token t; while (Next(t)) { if (aToken.Equals(t)) { found = true; break; } - if (t.Equals(Token::EndOfFile())) { + if (t.Equals(base::Token::EndOfFile())) { // We don't want to eat it. Rollback(); break; } } Claim(aResult, aInclude); mRollback = rollback; mRecord = record; return found; } +template<typename TChar> void -Tokenizer::Rollback() +TTokenizer<TChar>::Rollback() { - MOZ_ASSERT(mCursor > mRollback || mPastEof, - "Tokenizer::Rollback() cannot use twice or before any parsing"); + MOZ_ASSERT(base::mCursor > mRollback || base::mPastEof, "TODO!!!"); - mPastEof = false; - mHasFailed = false; - mCursor = mRollback; + base::mPastEof = false; + base::mHasFailed = false; + base::mCursor = mRollback; } +template<typename TChar> void -Tokenizer::Record(ClaimInclusion aInclude) +TTokenizer<TChar>::Record(ClaimInclusion aInclude) { mRecord = aInclude == INCLUDE_LAST ? mRollback - : mCursor; + : base::mCursor; } +template<typename TChar> void -Tokenizer::Claim(nsACString& aResult, ClaimInclusion aInclusion) +TTokenizer<TChar>::Claim(typename base::TAString& aResult, ClaimInclusion aInclusion) { - nsACString::const_char_iterator close = aInclusion == EXCLUDE_LAST + typename base::TAString::const_char_iterator close = aInclusion == EXCLUDE_LAST ? mRollback - : mCursor; + : base::mCursor; aResult.Assign(Substring(mRecord, close)); } +template<typename TChar> void -Tokenizer::Claim(nsDependentCSubstring& aResult, ClaimInclusion aInclusion) +TTokenizer<TChar>::Claim(typename base::TDependentSubstring& aResult, ClaimInclusion aInclusion) { - nsACString::const_char_iterator close = aInclusion == EXCLUDE_LAST + typename base::TAString::const_char_iterator close = aInclusion == EXCLUDE_LAST ? mRollback - : mCursor; + : base::mCursor; MOZ_RELEASE_ASSERT(close >= mRecord, "Overflow!"); aResult.Rebind(mRecord, close - mRecord); } // TokenizerBase -TokenizerBase::TokenizerBase(const char* aWhitespaces, - const char* aAdditionalWordChars) +template<typename TChar> +TokenizerBase<TChar>::TokenizerBase(const TChar* aWhitespaces, + const TChar* aAdditionalWordChars) : mPastEof(false) , mHasFailed(false) , mInputFinished(true) , mMode(Mode::FULL) , mMinRawDelivery(1024) , mWhitespaces(aWhitespaces ? aWhitespaces : sWhitespaces) , mAdditionalWordChars(aAdditionalWordChars) , mCursor(nullptr) , mEnd(nullptr) , mNextCustomTokenID(TOKEN_CUSTOM0) { } -TokenizerBase::Token -TokenizerBase::AddCustomToken(const nsACString & aValue, - ECaseSensitivity aCaseInsensitivity, bool aEnabled) +template<typename TChar> +auto +TokenizerBase<TChar>::AddCustomToken(const TAString & aValue, + ECaseSensitivity aCaseInsensitivity, bool aEnabled) + -> Token { MOZ_ASSERT(!aValue.IsEmpty()); UniquePtr<Token>& t = *mCustomTokens.AppendElement(); t = MakeUnique<Token>(); t->mType = static_cast<TokenType>(++mNextCustomTokenID); t->mCustomCaseInsensitivity = aCaseInsensitivity; t->mCustomEnabled = aEnabled; t->mCustom.Assign(aValue); return *t; } +template<typename TChar> void -TokenizerBase::RemoveCustomToken(Token& aToken) +TokenizerBase<TChar>::RemoveCustomToken(Token& aToken) { if (aToken.mType == TOKEN_UNKNOWN) { // Already removed return; } for (UniquePtr<Token> const& custom : mCustomTokens) { if (custom->mType == aToken.mType) { @@ -324,18 +360,19 @@ TokenizerBase::RemoveCustomToken(Token& aToken.mType = TOKEN_UNKNOWN; return; } } MOZ_ASSERT(false, "Token to remove not found"); } +template<typename TChar> void -TokenizerBase::EnableCustomToken(Token const& aToken, bool aEnabled) +TokenizerBase<TChar>::EnableCustomToken(Token const& aToken, bool aEnabled) { if (aToken.mType == TOKEN_UNKNOWN) { // Already removed return; } for (UniquePtr<Token> const& custom : mCustomTokens) { if (custom->Type() == aToken.Type()) { @@ -343,63 +380,68 @@ TokenizerBase::EnableCustomToken(Token c custom->mCustomEnabled = aEnabled; return; } } MOZ_ASSERT(false, "Token to change not found"); } +template<typename TChar> void -TokenizerBase::SetTokenizingMode(Mode aMode) +TokenizerBase<TChar>::SetTokenizingMode(Mode aMode) { mMode = aMode; } +template<typename TChar> bool -TokenizerBase::HasFailed() const +TokenizerBase<TChar>::HasFailed() const { return mHasFailed; } +template<typename TChar> bool -TokenizerBase::HasInput() const +TokenizerBase<TChar>::HasInput() const { return !mPastEof; } -nsACString::const_char_iterator -TokenizerBase::Parse(Token& aToken) const +template<typename TChar> +auto +TokenizerBase<TChar>::Parse(Token& aToken) const + -> typename TAString::const_char_iterator { if (mCursor == mEnd) { if (!mInputFinished) { return mCursor; } aToken = Token::EndOfFile(); return mEnd; } MOZ_RELEASE_ASSERT(mEnd >= mCursor, "Overflow!"); - nsACString::size_type available = mEnd - mCursor; + typename TAString::size_type available = mEnd - mCursor; uint32_t longestCustom = 0; for (UniquePtr<Token> const& custom : mCustomTokens) { if (IsCustom(mCursor, *custom, &longestCustom)) { aToken = *custom; return mCursor + custom->mCustom.Length(); } } if (!mInputFinished && available < longestCustom) { // Not enough data to deterministically decide. return mCursor; } - nsACString::const_char_iterator next = mCursor; + typename TAString::const_char_iterator next = mCursor; if (mMode == Mode::CUSTOM_ONLY) { // We have to do a brute-force search for all of the enabled custom // tokens. while (next < mEnd) { ++next; for (UniquePtr<Token> const& custom : mCustomTokens) { if (IsCustom(next, *custom)) { @@ -436,17 +478,17 @@ TokenizerBase::Parse(Token& aToken) cons PARSE_WS, PARSE_CHAR, } state; if (IsWordFirst(*next)) { state = PARSE_WORD; } else if (IsNumber(*next)) { state = PARSE_INTEGER; - } else if (strchr(mWhitespaces, *next)) { // not UTF-8 friendly? + } else if (contains(mWhitespaces, *next)) { // not UTF-8 friendly? state = PARSE_WS; } else if (*next == '\r') { state = PARSE_CRLF; } else if (*next == '\n') { state = PARSE_LF; } else { state = PARSE_CHAR; } @@ -512,55 +554,69 @@ TokenizerBase::Parse(Token& aToken) cons return next; } // switch (state) } // while (next < end) MOZ_ASSERT(!mInputFinished); return mCursor; } +template<typename TChar> bool -TokenizerBase::IsEnd(const nsACString::const_char_iterator& caret) const +TokenizerBase<TChar>::IsEnd(const typename TAString::const_char_iterator& caret) const { return caret == mEnd; } +template<typename TChar> bool -TokenizerBase::IsPending(const nsACString::const_char_iterator& caret) const +TokenizerBase<TChar>::IsPending(const typename TAString::const_char_iterator& caret) const { return IsEnd(caret) && !mInputFinished; } +template<typename TChar> bool -TokenizerBase::IsWordFirst(const char aInput) const +TokenizerBase<TChar>::IsWordFirst(const TChar aInput) const { // TODO: make this fully work with unicode return (ToLowerCase(static_cast<uint32_t>(aInput)) != ToUpperCase(static_cast<uint32_t>(aInput))) || '_' == aInput || - (mAdditionalWordChars ? !!strchr(mAdditionalWordChars, aInput) : false); + (mAdditionalWordChars ? contains(mAdditionalWordChars, aInput) : false); } +template<typename TChar> bool -TokenizerBase::IsWord(const char aInput) const +TokenizerBase<TChar>::IsWord(const TChar aInput) const { return IsWordFirst(aInput) || IsNumber(aInput); } +template<typename TChar> bool -TokenizerBase::IsNumber(const char aInput) const +TokenizerBase<TChar>::IsNumber(const TChar aInput) const { // TODO: are there unicode numbers? return aInput >= '0' && aInput <= '9'; } +namespace { + +template<typename TChar> class TCharComparator; +template<> class TCharComparator<char> final : public nsCaseInsensitiveUTF8StringComparator {}; +template<> class TCharComparator<char16_t> final : public nsCaseInsensitiveStringComparator {}; + +} + +template<typename TChar> bool -TokenizerBase::IsCustom(const nsACString::const_char_iterator & caret, - const Token & aCustomToken, - uint32_t * aLongest) const +TokenizerBase<TChar>::IsCustom(const typename TAString::const_char_iterator & caret, + const Token & aCustomToken, + uint32_t * aLongest) const { MOZ_ASSERT(aCustomToken.mType > TOKEN_CUSTOM0); if (!aCustomToken.mCustomEnabled) { return false; } if (aLongest) { *aLongest = std::max(*aLongest, aCustomToken.mCustom.Length()); @@ -570,153 +626,169 @@ TokenizerBase::IsCustom(const nsACString // and since it's on a hot path, it's just a diagnostic assert, // not a release assert. MOZ_DIAGNOSTIC_ASSERT(mEnd >= caret, "Overflow?"); uint32_t inputLength = mEnd - caret; if (aCustomToken.mCustom.Length() > inputLength) { return false; } - nsDependentCSubstring inputFragment(caret, aCustomToken.mCustom.Length()); + TDependentSubstring inputFragment(caret, aCustomToken.mCustom.Length()); if (aCustomToken.mCustomCaseInsensitivity == CASE_INSENSITIVE) { - return inputFragment.Equals(aCustomToken.mCustom, nsCaseInsensitiveUTF8StringComparator()); + return inputFragment.Equals(aCustomToken.mCustom, TCharComparator<TChar>()); } return inputFragment.Equals(aCustomToken.mCustom); } -void TokenizerBase::AssignFragment(Token& aToken, - nsACString::const_char_iterator begin, - nsACString::const_char_iterator end) +template<typename TChar> +void +TokenizerBase<TChar>::AssignFragment(Token& aToken, + typename TAString::const_char_iterator begin, + typename TAString::const_char_iterator end) { aToken.AssignFragment(begin, end); } // TokenizerBase::Token -TokenizerBase::Token::Token() +template<typename TChar> +TokenizerBase<TChar>::Token::Token() : mType(TOKEN_UNKNOWN) , mChar(0) , mInteger(0) , mCustomCaseInsensitivity(CASE_SENSITIVE) , mCustomEnabled(false) { } -TokenizerBase::Token::Token(const Token& aOther) +template<typename TChar> +TokenizerBase<TChar>::Token::Token(const Token& aOther) : mType(aOther.mType) , mCustom(aOther.mCustom) , mChar(aOther.mChar) , mInteger(aOther.mInteger) , mCustomCaseInsensitivity(aOther.mCustomCaseInsensitivity) , mCustomEnabled(aOther.mCustomEnabled) { if (mType == TOKEN_WORD || mType > TOKEN_CUSTOM0) { mWord.Rebind(aOther.mWord.BeginReading(), aOther.mWord.Length()); } } -TokenizerBase::Token& -TokenizerBase::Token::operator=(const Token& aOther) +template<typename TChar> +auto +TokenizerBase<TChar>::Token::operator=(const Token& aOther) + -> Token& { mType = aOther.mType; mCustom = aOther.mCustom; mChar = aOther.mChar; mWord.Rebind(aOther.mWord.BeginReading(), aOther.mWord.Length()); mInteger = aOther.mInteger; mCustomCaseInsensitivity = aOther.mCustomCaseInsensitivity; mCustomEnabled = aOther.mCustomEnabled; return *this; } +template<typename TChar> void -TokenizerBase::Token::AssignFragment(nsACString::const_char_iterator begin, - nsACString::const_char_iterator end) +TokenizerBase<TChar>::Token::AssignFragment(typename TAString::const_char_iterator begin, + typename TAString::const_char_iterator end) { MOZ_RELEASE_ASSERT(end >= begin, "Overflow!"); mFragment.Rebind(begin, end - begin); } // static -TokenizerBase::Token -TokenizerBase::Token::Raw() +template<typename TChar> +auto +TokenizerBase<TChar>::Token::Raw() -> Token { Token t; t.mType = TOKEN_RAW; return t; } // static -TokenizerBase::Token -TokenizerBase::Token::Word(const nsACString& aValue) +template<typename TChar> +auto +TokenizerBase<TChar>::Token::Word(TAString const& aValue) -> Token { Token t; t.mType = TOKEN_WORD; t.mWord.Rebind(aValue.BeginReading(), aValue.Length()); return t; } // static -TokenizerBase::Token -TokenizerBase::Token::Char(const char aValue) +template<typename TChar> +auto +TokenizerBase<TChar>::Token::Char(TChar const aValue) -> Token { Token t; t.mType = TOKEN_CHAR; t.mChar = aValue; return t; } // static -TokenizerBase::Token -TokenizerBase::Token::Number(const uint64_t aValue) +template<typename TChar> +auto +TokenizerBase<TChar>::Token::Number(uint64_t const aValue) -> Token { Token t; t.mType = TOKEN_INTEGER; t.mInteger = aValue; return t; } // static -TokenizerBase::Token -TokenizerBase::Token::Whitespace() +template<typename TChar> +auto +TokenizerBase<TChar>::Token::Whitespace() -> Token { Token t; t.mType = TOKEN_WS; t.mChar = '\0'; return t; } // static -TokenizerBase::Token -TokenizerBase::Token::NewLine() +template<typename TChar> +auto +TokenizerBase<TChar>::Token::NewLine() -> Token { Token t; t.mType = TOKEN_EOL; return t; } // static -TokenizerBase::Token -TokenizerBase::Token::EndOfFile() +template<typename TChar> +auto +TokenizerBase<TChar>::Token::EndOfFile() -> Token { Token t; t.mType = TOKEN_EOF; return t; } // static -TokenizerBase::Token -TokenizerBase::Token::Error() +template<typename TChar> +auto +TokenizerBase<TChar>::Token::Error() -> Token { Token t; t.mType = TOKEN_ERROR; return t; } +template<typename TChar> bool -TokenizerBase::Token::Equals(const Token& aOther) const +TokenizerBase<TChar>::Token::Equals(const Token& aOther) const { if (mType != aOther.mType) { return false; } switch (mType) { case TOKEN_INTEGER: return AsInteger() == aOther.AsInteger(); @@ -724,30 +796,39 @@ TokenizerBase::Token::Equals(const Token return AsString() == aOther.AsString(); case TOKEN_CHAR: return AsChar() == aOther.AsChar(); default: return true; } } -char -TokenizerBase::Token::AsChar() const +template<typename TChar> +TChar +TokenizerBase<TChar>::Token::AsChar() const { MOZ_ASSERT(mType == TOKEN_CHAR || mType == TOKEN_WS); return mChar; } -nsDependentCSubstring -TokenizerBase::Token::AsString() const +template<typename TChar> +auto +TokenizerBase<TChar>::Token::AsString() const -> TDependentSubstring { MOZ_ASSERT(mType == TOKEN_WORD); return mWord; } +template<typename TChar> uint64_t -TokenizerBase::Token::AsInteger() const +TokenizerBase<TChar>::Token::AsInteger() const { MOZ_ASSERT(mType == TOKEN_INTEGER); return mInteger; } +template class TokenizerBase<char>; +template class TokenizerBase<char16_t>; + +template class TTokenizer<char>; +template class TTokenizer<char16_t>; + } // mozilla
--- a/xpcom/ds/Tokenizer.h +++ b/xpcom/ds/Tokenizer.h @@ -11,19 +11,27 @@ #include "mozilla/CheckedInt.h" #include "mozilla/ScopeExit.h" #include "mozilla/TypeTraits.h" #include "mozilla/UniquePtr.h" #include "nsTArray.h" namespace mozilla { +template <typename TChar> class TokenizerBase { public: + typedef nsTSubstring<TChar> TAString; + typedef nsTString<TChar> TString; + typedef nsTDependentString<TChar> TDependentString; + typedef nsTDependentSubstring<TChar> TDependentSubstring; + + static TChar const sWhitespaces[]; + /** * The analyzer works with elements in the input cut to a sequence of token * where each token has an elementary type */ enum TokenType : uint32_t { TOKEN_UNKNOWN, TOKEN_RAW, @@ -40,77 +48,77 @@ public: enum ECaseSensitivity { CASE_SENSITIVE, CASE_INSENSITIVE }; /** * Class holding the type and the value of a token. It can be manually created - * to allow checks against it via methods of Tokenizer or are results of some of - * the Tokenizer's methods. + * to allow checks against it via methods of TTokenizer or are results of some of + * the TTokenizer's methods. */ class Token { TokenType mType; - nsDependentCSubstring mWord; - nsCString mCustom; - char mChar; + TDependentSubstring mWord; + TString mCustom; + TChar mChar; uint64_t mInteger; ECaseSensitivity mCustomCaseInsensitivity; bool mCustomEnabled; // If this token is a result of the parsing process, this member is referencing // a sub-string in the input buffer. If this is externally created Token this // member is left an empty string. - nsDependentCSubstring mFragment; + TDependentSubstring mFragment; - friend class TokenizerBase; - void AssignFragment(nsACString::const_char_iterator begin, - nsACString::const_char_iterator end); + friend class TokenizerBase<TChar>; + void AssignFragment(typename TAString::const_char_iterator begin, + typename TAString::const_char_iterator end); static Token Raw(); public: Token(); Token(const Token& aOther); Token& operator=(const Token& aOther); // Static constructors of tokens by type and value - static Token Word(const nsACString& aWord); - static Token Char(const char aChar); - static Token Number(const uint64_t aNumber); + static Token Word(TAString const& aWord); + static Token Char(TChar const aChar); + static Token Number(uint64_t const aNumber); static Token Whitespace(); static Token NewLine(); static Token EndOfFile(); static Token Error(); // Compares the two tokens, type must be identical and value // of one of the tokens must be 'any' or equal. bool Equals(const Token& aOther) const; TokenType Type() const { return mType; } - char AsChar() const; - nsDependentCSubstring AsString() const; + TChar AsChar() const; + TDependentSubstring AsString() const; uint64_t AsInteger() const; - nsDependentCSubstring Fragment() const { return mFragment; } + TDependentSubstring Fragment() const { return mFragment; } }; /** * Consumers may register a custom string that, when found in the input, is considered * a token and returned by Next*() and accepted by Check*() methods. * AddCustomToken() returns a reference to a token that can then be comapred using * Token::Equals() againts the output from Next*() or be passed to Check*(). */ - Token AddCustomToken(const nsACString& aValue, ECaseSensitivity aCaseInsensitivity, bool aEnabled = true); + Token AddCustomToken(const TAString& aValue, ECaseSensitivity aCaseInsensitivity, bool aEnabled = true); template <uint32_t N> - Token AddCustomToken(const char(&aValue)[N], ECaseSensitivity aCaseInsensitivity, bool aEnabled = true) + Token AddCustomToken(const TChar(&aValue)[N], ECaseSensitivity aCaseInsensitivity, bool aEnabled = true) { - return AddCustomToken(nsDependentCSubstring(aValue, N - 1), aCaseInsensitivity, aEnabled); + return AddCustomToken(TDependentSubstring(aValue, N - 1), aCaseInsensitivity, aEnabled); } void RemoveCustomToken(Token& aToken); /** * Only applies to a custom type of a Token (see AddCustomToken above.) * This turns on and off token recognition. When a custom token is disabled, * it's ignored as never added as a custom token. */ void EnableCustomToken(Token const& aToken, bool aEnable); @@ -131,138 +139,143 @@ public: /** * Return false iff the last Check*() call has returned false or when we've read past * the end of the input string. */ MOZ_MUST_USE bool HasFailed() const; protected: - explicit TokenizerBase(const char* aWhitespaces = nullptr, - const char* aAdditionalWordChars = nullptr); + explicit TokenizerBase(const TChar* aWhitespaces = nullptr, + const TChar* aAdditionalWordChars = nullptr); // false if we have already read the EOF token. bool HasInput() const; // Main parsing function, it doesn't shift the read cursor, just returns the next // token position. - nsACString::const_char_iterator Parse(Token& aToken) const; + typename TAString::const_char_iterator Parse(Token& aToken) const; // Is read cursor at the end? - bool IsEnd(const nsACString::const_char_iterator& caret) const; + bool IsEnd(const typename TAString::const_char_iterator& caret) const; // True, when we are at the end of the input data, but it has not been marked - // as complete yet. In that case we cannot proceed with providing a multi-char token. - bool IsPending(const nsACString::const_char_iterator & caret) const; + // as complete yet. In that case we cannot proceed with providing a multi-TChar token. + bool IsPending(const typename TAString::const_char_iterator & caret) const; // Is read cursor on a character that is a word start? - bool IsWordFirst(const char aInput) const; + bool IsWordFirst(const TChar aInput) const; // Is read cursor on a character that is an in-word letter? - bool IsWord(const char aInput) const; + bool IsWord(const TChar aInput) const; // Is read cursor on a character that is a valid number? // TODO - support multiple radix - bool IsNumber(const char aInput) const; + bool IsNumber(const TChar aInput) const; // Is equal to the given custom token? - bool IsCustom(const nsACString::const_char_iterator& caret, + bool IsCustom(const typename TAString::const_char_iterator& caret, const Token& aCustomToken, uint32_t* aLongest = nullptr) const; // Friendly helper to assign a fragment on a Token static void AssignFragment(Token& aToken, - nsACString::const_char_iterator begin, - nsACString::const_char_iterator end); + typename TAString::const_char_iterator begin, + typename TAString::const_char_iterator end); // true iff we have already read the EOF token bool mPastEof; // true iff the last Check*() call has returned false, reverts to true on Rollback() call bool mHasFailed; // true if the input string is final (finished), false when we expect more data // yet to be fed to the tokenizer (see IncrementalTokenizer derived class). bool mInputFinished; // custom only vs full tokenizing mode, see the Parse() method Mode mMode; // minimal raw data chunked delivery during incremental feed uint32_t mMinRawDelivery; // Customizable list of whitespaces - const char* mWhitespaces; + const TChar* mWhitespaces; // Additinal custom word characters - const char* mAdditionalWordChars; + const TChar* mAdditionalWordChars; // All these point to the original buffer passed to the constructor or to the incremental // buffer after FeedInput. - nsACString::const_char_iterator mCursor; // Position of the current (actually next to read) token start - nsACString::const_char_iterator mEnd; // End of the input position + typename TAString::const_char_iterator mCursor; // Position of the current (actually next to read) token start + typename TAString::const_char_iterator mEnd; // End of the input position // This is the list of tokens user has registered with AddCustomToken() nsTArray<UniquePtr<Token>> mCustomTokens; uint32_t mNextCustomTokenID; private: TokenizerBase() = delete; TokenizerBase(const TokenizerBase&) = delete; TokenizerBase(TokenizerBase&&) = delete; TokenizerBase(const TokenizerBase&&) = delete; TokenizerBase &operator=(const TokenizerBase&) = delete; }; /** * This is a simple implementation of a lexical analyzer or maybe better - * called a tokenizer. It doesn't allow any user dictionaries or - * user define token types. + * called a tokenizer. * - * It is limited only to ASCII input for now. UTF-8 or any other input - * encoding must yet be implemented. + * Please use Tokenizer or Tokenizer16 classes, that are specializations + * of this template class. Tokenizer is for ASCII input, Tokenizer16 may + * handle char16_t input, but doesn't recognize whitespaces or numbers + * other than standard `char` specialized Tokenizer class. */ -class Tokenizer : public TokenizerBase +template <typename TChar> +class TTokenizer : public TokenizerBase<TChar> { public: + typedef TokenizerBase<TChar> base; + /** * @param aSource * The string to parse. - * IMPORTANT NOTE: Tokenizer doesn't ensure the input string buffer lifetime. - * It's up to the consumer to make sure the string's buffer outlives the Tokenizer! + * IMPORTANT NOTE: TTokenizer doesn't ensure the input string buffer + * lifetime. It's up to the consumer to make sure the string's buffer outlives + * the TTokenizer! * @param aWhitespaces - * If non-null Tokenizer will use this custom set of whitespaces for CheckWhite() - * and SkipWhites() calls. - * By default the list consists of space and tab. + * If non-null TTokenizer will use this custom set of whitespaces for + * CheckWhite() and SkipWhites() calls. By default the list consists of space + * and tab. * @param aAdditionalWordChars - * If non-null it will be added to the list of characters that consist a word. - * This is useful when you want to accept e.g. '-' in HTTP headers. - * By default a word character is consider any character for which upper case + * If non-null it will be added to the list of characters that consist a + * word. This is useful when you want to accept e.g. '-' in HTTP headers. By + * default a word character is consider any character for which upper case * is different from lower case. * - * If there is an overlap between aWhitespaces and aAdditionalWordChars, the check for - * word characters is made first. + * If there is an overlap between aWhitespaces and aAdditionalWordChars, the + * check for word characters is made first. */ - explicit Tokenizer(const nsACString& aSource, - const char* aWhitespaces = nullptr, - const char* aAdditionalWordChars = nullptr); - explicit Tokenizer(const char* aSource, - const char* aWhitespaces = nullptr, - const char* aAdditionalWordChars = nullptr); + explicit TTokenizer(const typename base::TAString& aSource, + const TChar* aWhitespaces = nullptr, + const TChar* aAdditionalWordChars = nullptr); + explicit TTokenizer(const TChar* aSource, + const TChar* aWhitespaces = nullptr, + const TChar* aAdditionalWordChars = nullptr); /** * When there is still anything to read from the input, tokenize it, store the token type * and value to aToken result and shift the cursor past this just parsed token. Each call * to Next() reads another token from the input and shifts the cursor. * Returns false if we have passed the end of the input. */ MOZ_MUST_USE - bool Next(Token& aToken); + bool Next(typename base::Token& aToken); /** * Parse the token on the input read cursor position, check its type is equal to aTokenType * and if so, put it into aResult, shift the cursor and return true. Otherwise, leave * the input read cursor position intact and return false. */ MOZ_MUST_USE - bool Check(const TokenType aTokenType, Token& aResult); + bool Check(const typename base::TokenType aTokenType, typename base::Token& aResult); /** * Same as above method, just compares both token type and token value passed in aToken. * When both the type and the value equals, shift the cursor and return true. Otherwise * return false. */ MOZ_MUST_USE - bool Check(const Token& aToken); + bool Check(const typename base::Token& aToken); /** * SkipWhites method (below) may also skip new line characters automatically. */ enum WhiteSkipping { /** * SkipWhites will only skip what is defined as a white space (default). */ @@ -279,98 +292,102 @@ public: * optionally skip also new lines. */ void SkipWhites(WhiteSkipping aIncludeNewLines = DONT_INCLUDE_NEW_LINE); /** * Skips all tokens until the given one is found or EOF is hit. The token * or EOF are next to read. */ - void SkipUntil(Token const& aToken); + void SkipUntil(typename base::Token const& aToken); // These are mostly shortcuts for the Check() methods above. /** * Check whitespace character is present. */ MOZ_MUST_USE - bool CheckWhite() { return Check(Token::Whitespace()); } + bool CheckWhite() { return Check(base::Token::Whitespace()); } /** * Check there is a single character on the read cursor position. If so, shift the read * cursor position and return true. Otherwise false. */ MOZ_MUST_USE - bool CheckChar(const char aChar) { return Check(Token::Char(aChar)); } + bool CheckChar(const TChar aChar) { return Check(base::Token::Char(aChar)); } /** * This is a customizable version of CheckChar. aClassifier is a function called with * value of the character on the current input read position. If this user function * returns true, read cursor is shifted and true returned. Otherwise false. * The user classifiction function is not called when we are at or past the end and * false is immediately returned. */ MOZ_MUST_USE - bool CheckChar(bool (*aClassifier)(const char aChar)); + bool CheckChar(bool (*aClassifier)(const TChar aChar)); /** * Check for a whole expected word. */ MOZ_MUST_USE - bool CheckWord(const nsACString& aWord) { return Check(Token::Word(aWord)); } + bool CheckWord(const typename base::TAString& aWord) { + return Check(base::Token::Word(aWord)); + } /** * Shortcut for literal const word check with compile time length calculation. */ template <uint32_t N> MOZ_MUST_USE - bool CheckWord(const char (&aWord)[N]) { return Check(Token::Word(nsDependentCString(aWord, N - 1))); } + bool CheckWord(const TChar (&aWord)[N]) { + return Check(base::Token::Word(typename base::TDependentString(aWord, N - 1))); + } /** * Checks \r, \n or \r\n. */ MOZ_MUST_USE - bool CheckEOL() { return Check(Token::NewLine()); } + bool CheckEOL() { return Check(base::Token::NewLine()); } /** * Checks we are at the end of the input string reading. If so, shift past the end * and returns true. Otherwise does nothing and returns false. */ MOZ_MUST_USE - bool CheckEOF() { return Check(Token::EndOfFile()); } + bool CheckEOF() { return Check(base::Token::EndOfFile()); } /** * These are shortcuts to obtain the value immediately when the token type matches. */ - MOZ_MUST_USE bool ReadChar(char* aValue); - MOZ_MUST_USE bool ReadChar(bool (*aClassifier)(const char aChar), - char* aValue); - MOZ_MUST_USE bool ReadWord(nsACString& aValue); - MOZ_MUST_USE bool ReadWord(nsDependentCSubstring& aValue); + MOZ_MUST_USE bool ReadChar(TChar* aValue); + MOZ_MUST_USE bool ReadChar(bool (*aClassifier)(const TChar aChar), + TChar* aValue); + MOZ_MUST_USE bool ReadWord(typename base::TAString& aValue); + MOZ_MUST_USE bool ReadWord(typename base::TDependentSubstring& aValue); /** * This is an integer read helper. It returns false and doesn't move the read * cursor when any of the following happens: * - the token at the read cursor is not an integer * - the final number doesn't fit the T type * Otherwise true is returned, aValue is filled with the integral number * and the cursor is moved forward. */ template <typename T> MOZ_MUST_USE bool ReadInteger(T *aValue) { MOZ_RELEASE_ASSERT(aValue); - nsACString::const_char_iterator rollback = mRollback; - nsACString::const_char_iterator cursor = mCursor; - Token t; - if (!Check(TOKEN_INTEGER, t)) { + typename base::TAString::const_char_iterator rollback = mRollback; + typename base::TAString::const_char_iterator cursor = base::mCursor; + typename base::Token t; + if (!Check(base::TOKEN_INTEGER, t)) { return false; } mozilla::CheckedInt<T> checked(t.AsInteger()); if (!checked.isValid()) { // Move to a state as if Check() call has failed mRollback = rollback; - mCursor = cursor; - mHasFailed = true; + base::mCursor = cursor; + base::mHasFailed = true; return false; } *aValue = checked.value(); return true; } /** @@ -378,31 +395,31 @@ public: */ template <typename T, typename V = typename EnableIf<IsSigned<typename RemovePointer<T>::Type>::value, typename RemovePointer<T>::Type>::Type> MOZ_MUST_USE bool ReadSignedInteger(T *aValue) { MOZ_RELEASE_ASSERT(aValue); - nsACString::const_char_iterator rollback = mRollback; - nsACString::const_char_iterator cursor = mCursor; + typename base::TAString::const_char_iterator rollback = mRollback; + typename base::TAString::const_char_iterator cursor = base::mCursor; auto revert = MakeScopeExit([&] { // Move to a state as if Check() call has failed mRollback = rollback; - mCursor = cursor; - mHasFailed = true; + base::mCursor = cursor; + base::mHasFailed = true; }); // Using functional raw access because '-' could be part of the word set // making CheckChar('-') not work. - bool minus = CheckChar([](const char aChar) { return aChar == '-'; }); + bool minus = CheckChar([](const TChar aChar) { return aChar == '-'; }); - Token t; - if (!Check(TOKEN_INTEGER, t)) { + typename base::Token t; + if (!Check(base::TOKEN_INTEGER, t)) { return false; } mozilla::CheckedInt<T> checked(t.AsInteger()); if (minus) { checked *= -1; } @@ -412,28 +429,28 @@ public: *aValue = checked.value(); revert.release(); return true; } /** * Returns the read cursor position back as it was before the last call of any parsing - * method of Tokenizer (Next, Check*, Skip*, Read*) so that the last operation + * method of TTokenizer (Next, Check*, Skip*, Read*) so that the last operation * can be repeated. * Rollback cannot be used multiple times, it only reverts the last successfull parse * operation. It also cannot be used before any parsing operation has been called - * on the Tokenizer. + * on the TTokenizer. */ void Rollback(); /** * Record() and Claim() are collecting the input as it is being parsed to obtain * a substring between particular syntax bounderies defined by any recursive - * descent parser or simple parser the Tokenizer is used to read the input for. + * descent parser or simple parser the TTokenizer is used to read the input for. * Inlucsion of a token that has just been parsed can be controlled using an arguemnt. */ enum ClaimInclusion { /** * Include resulting (or passed) token of the last lexical analyzer operation in the result. */ INCLUDE_LAST, /** @@ -448,43 +465,46 @@ public: * parsed token (INCLUDE_LAST). */ void Record(ClaimInclusion aInclude = EXCLUDE_LAST); /** * Claim result of the record started with Record() call before. Depending on aInclude * the ending of the sub-string result includes or excludes the last parsed or checked * token. */ - void Claim(nsACString& aResult, ClaimInclusion aInclude = EXCLUDE_LAST); - void Claim(nsDependentCSubstring& aResult, ClaimInclusion aInclude = EXCLUDE_LAST); + void Claim(typename base::TAString& aResult, ClaimInclusion aInclude = EXCLUDE_LAST); + void Claim(typename base::TDependentSubstring& aResult, ClaimInclusion aInclude = EXCLUDE_LAST); /** * If aToken is found, aResult is set to the substring between the current * position and the position of aToken, potentially including aToken depending * on aInclude. * If aToken isn't found aResult is set to the substring between the current * position and the end of the string. * If aToken is found, the method returns true. Otherwise it returns false. * * Calling Rollback() after ReadUntil() will return the read cursor to the * position it had before ReadUntil was called. */ - MOZ_MUST_USE bool ReadUntil(Token const& aToken, nsDependentCSubstring& aResult, + MOZ_MUST_USE bool ReadUntil(typename base::Token const& aToken, typename base::TDependentSubstring& aResult, ClaimInclusion aInclude = EXCLUDE_LAST); - MOZ_MUST_USE bool ReadUntil(Token const& aToken, nsACString& aResult, + MOZ_MUST_USE bool ReadUntil(typename base::Token const& aToken, typename base::TAString& aResult, ClaimInclusion aInclude = EXCLUDE_LAST); protected: - // All these point to the original buffer passed to the Tokenizer's constructor - nsACString::const_char_iterator mRecord; // Position where the recorded sub-string for Claim() is - nsACString::const_char_iterator mRollback; // Position of the previous token start + // All these point to the original buffer passed to the TTokenizer's constructor + typename base::TAString::const_char_iterator mRecord; // Position where the recorded sub-string for Claim() is + typename base::TAString::const_char_iterator mRollback; // Position of the previous token start private: - Tokenizer() = delete; - Tokenizer(const Tokenizer&) = delete; - Tokenizer(Tokenizer&&) = delete; - Tokenizer(const Tokenizer&&) = delete; - Tokenizer &operator=(const Tokenizer&) = delete; + TTokenizer() = delete; + TTokenizer(const TTokenizer&) = delete; + TTokenizer(TTokenizer&&) = delete; + TTokenizer(const TTokenizer&&) = delete; + TTokenizer &operator=(const TTokenizer&) = delete; }; +typedef TTokenizer<char> Tokenizer; +typedef TTokenizer<char16_t> Tokenizer16; + } // mozilla #endif // Tokenizer_h__
--- a/xpcom/io/InputStreamLengthHelper.cpp +++ b/xpcom/io/InputStreamLengthHelper.cpp @@ -1,16 +1,17 @@ /* -*- 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 "InputStreamLengthHelper.h" #include "mozilla/dom/WorkerCommon.h" +#include "nsIAsyncInputStream.h" #include "nsIInputStream.h" #include "nsIStreamTransportService.h" static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID); namespace mozilla { namespace { @@ -45,17 +46,19 @@ public: nsCOMPtr<nsIRunnable> self(this); // overly cute mCallbackTarget->Dispatch(self.forget(), NS_DISPATCH_NORMAL); mCallbackTarget = nullptr; return NS_OK; } // pong - mCallback(mSize); + std::function<void(int64_t aLength)> callback; + callback.swap(mCallback); + callback(mSize); return NS_OK; } private: nsCOMPtr<nsIInputStream> mStream; std::function<void(int64_t aLength)> mCallback; nsCOMPtr<nsIEventTarget> mCallbackTarget; @@ -95,16 +98,22 @@ InputStreamLengthHelper::GetSyncLength(n nsCOMPtr<nsIAsyncInputStreamLength> asyncStreamLength = do_QueryInterface(aStream); if (asyncStreamLength) { // GetAsyncLength should be used. return false; } + // We cannot calculate the length of an async stream. + nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(aStream); + if (asyncStream) { + return false; + } + // For main-thread only, we want to avoid calling ::Available() for blocking // streams. if (NS_IsMainThread()) { bool nonBlocking = false; if (NS_WARN_IF(NS_FAILED(aStream->IsNonBlocking(&nonBlocking)))) { // Let's return -1. There is nothing else we can do here. return true; }