diff --git a/browser/app/Makefile.in b/browser/app/Makefile.in index bfe906d821bc..767353a26f02 100644 --- a/browser/app/Makefile.in +++ b/browser/app/Makefile.in @@ -79,7 +79,7 @@ clean clobber repackage:: MAC_BUNDLE_VERSION = $(shell $(PYTHON) $(srcdir)/macversion.py --version=$(MOZ_APP_VERSION) --buildid=$(DEPTH)/buildid.h) .PHONY: repackage -tools repackage:: $(DIST)/bin/$(MOZ_APP_NAME) features +tools repackage:: $(DIST)/bin/$(MOZ_APP_NAME) rm -rf $(dist_dest) $(MKDIR) -p '$(dist_dest)/Contents/MacOS' $(MKDIR) -p '$(dist_dest)/$(LPROJ)' @@ -100,7 +100,3 @@ ifdef MOZ_UPDATER endif printf APPLMOZB > '$(dist_dest)/Contents/PkgInfo' endif - -.PHONY: features -tools features:: - $(call py_action,generate_builtin_addons,--features=browser/features browser/chrome/browser/content/browser/built_in_addons.json) diff --git a/config/faster/rules.mk b/config/faster/rules.mk index 7ae9947a5fcd..3ca16dad317e 100644 --- a/config/faster/rules.mk +++ b/config/faster/rules.mk @@ -96,23 +96,3 @@ $(addprefix install-,$(INSTALL_MANIFESTS)): install-%: $(addprefix $(TOPOBJDIR)/ # that are not supported by data in moz.build. $(TOPOBJDIR)/build/application.ini: $(TOPOBJDIR)/buildid.h $(TOPOBJDIR)/source-repo.h - -# The manifest of allowed system add-ons should be re-built when using -# "build faster". -# -# Note the dependency on install-dist/bin. The form of this -# dependency is critical: it's triggering the stem rule (install-%) -# above to force the dist/bin manifest to be processed. The more -# obvious `$(TOPOBJDIR)/install-dist_bin` doesn't work because -# dist/bin isn't in $(INSTALL_MANIFESTS) in the -# FasterMake+RecursiveMake (artifact build) situation. -ifeq ($(MOZ_BUILD_APP),browser) -$(TOPOBJDIR)/browser/app/features: install-dist/bin - -default: $(TOPOBJDIR)/browser/app/features -endif -ifeq ($(MOZ_BUILD_APP),mobile/android) -$(TOPOBJDIR)/mobile/android/base/features: install-dist/bin - -default: $(TOPOBJDIR)/mobile/android/base/features -endif diff --git a/devtools/client/debugger/new/src/actions/ast.js b/devtools/client/debugger/new/src/actions/ast.js index ef445bd41462..a244eea3fce2 100644 --- a/devtools/client/debugger/new/src/actions/ast.js +++ b/devtools/client/debugger/new/src/actions/ast.js @@ -101,12 +101,14 @@ function setOutOfScopeLocations() { } function compressPausePoints(pausePoints) { - const compressed = {} + const compressed = {}; + for (const line in pausePoints) { - compressed[line] = {} + compressed[line] = {}; + for (const col in pausePoints[line]) { - const point = pausePoints[line][col] - compressed[line][col] = (point.break && 1) | (point.step && 2) + const point = pausePoints[line][col]; + compressed[line][col] = (point.break && 1) | (point.step && 2); } } diff --git a/devtools/client/debugger/new/src/actions/preview.js b/devtools/client/debugger/new/src/actions/preview.js index 14dd38c2bbd3..91ba7c28c989 100644 --- a/devtools/client/debugger/new/src/actions/preview.js +++ b/devtools/client/debugger/new/src/actions/preview.js @@ -11,8 +11,6 @@ var _preview = require("../utils/preview"); var _ast = require("../utils/ast"); -var _editor = require("../utils/editor/index"); - var _devtoolsSourceMap = require("devtools/client/shared/source-map/index.js"); var _promise = require("./utils/middleware/promise"); @@ -25,74 +23,37 @@ var _expressions = require("./expressions"); var _pause = require("./pause/index"); -var _lodash = require("devtools/client/shared/vendor/lodash"); - /* 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 . */ -function isInvalidTarget(target) { - if (!target || !target.innerText) { - return true; +function findExpressionMatch(state, codeMirror, tokenPos) { + const source = (0, _selectors.getSelectedSource)(state); + const symbols = (0, _selectors.getSymbols)(state, source); + let match; + + if (!symbols || symbols.loading) { + match = (0, _getExpression.getExpressionFromCoords)(codeMirror, tokenPos); + } else { + match = (0, _ast.findBestMatchExpression)(symbols, tokenPos); } - const tokenText = target.innerText.trim(); - const cursorPos = target.getBoundingClientRect(); // exclude literal tokens where it does not make sense to show a preview - - const invalidType = ["cm-atom", ""].includes(target.className); // exclude syntax where the expression would be a syntax error - - const invalidToken = tokenText === "" || tokenText.match(/^[(){}\|&%,.;=<>\+-/\*\s](?=)/); - const isPresentation = target.attributes.role && target.attributes.getNamedItem("role").value == "presentation"; // exclude codemirror elements that are not tokens - - const invalidTarget = target.parentElement && !target.parentElement.closest(".CodeMirror-line") || cursorPos.top == 0; - return invalidTarget || invalidToken || invalidType || isPresentation; + return match; } -function updatePreview(target, editor) { +function updatePreview(target, tokenPos, codeMirror) { return ({ dispatch, getState, client, sourceMaps }) => { - const tokenPos = (0, _editor.getTokenLocation)(editor.codeMirror, target); const cursorPos = target.getBoundingClientRect(); - const preview = (0, _selectors.getPreview)(getState()); - if ((0, _selectors.getCanRewind)(getState())) { + if ((0, _selectors.getCanRewind)(getState()) || !(0, _selectors.isSelectedFrameVisible)(getState()) || !(0, _selectors.isLineInScope)(getState(), tokenPos.line)) { return; } - if (preview) { - // Return early if we are currently showing another preview or - // if we are mousing over the same token as before - if (preview.updating || (0, _lodash.isEqual)(preview.tokenPos, tokenPos)) { - return; - } // We are mousing over a new token that is not in the preview - - - if (!target.classList.contains("debug-expression")) { - dispatch(clearPreview()); - } - } - - if (isInvalidTarget(target)) { - dispatch(clearPreview()); - return; - } - - if (!(0, _selectors.isSelectedFrameVisible)(getState()) || !(0, _selectors.isLineInScope)(getState(), tokenPos.line)) { - return; - } - - const source = (0, _selectors.getSelectedSource)(getState()); - const symbols = (0, _selectors.getSymbols)(getState(), source); - let match; - - if (!symbols || symbols.loading) { - match = (0, _getExpression.getExpressionFromCoords)(editor.codeMirror, tokenPos); - } else { - match = (0, _ast.findBestMatchExpression)(symbols, tokenPos); - } + const match = findExpressionMatch(getState(), codeMirror, tokenPos); if (!match) { return; diff --git a/devtools/client/debugger/new/src/components/Editor/Preview/Popup.js b/devtools/client/debugger/new/src/components/Editor/Preview/Popup.js index 312760caf278..0eb7a4026f63 100644 --- a/devtools/client/debugger/new/src/components/Editor/Preview/Popup.js +++ b/devtools/client/debugger/new/src/components/Editor/Preview/Popup.js @@ -29,8 +29,6 @@ var _PreviewFunction = require("../../shared/PreviewFunction"); var _PreviewFunction2 = _interopRequireDefault(_PreviewFunction); -var _editor = require("../../../utils/editor/index"); - var _preview = require("../../../utils/preview"); var _Svg = require("devtools/client/debugger/new/dist/vendors").vendored["Svg"]; @@ -63,6 +61,18 @@ const { loadItemProperties } = ObjectInspectorUtils.loadProperties; +function inPreview(event) { + const relatedTarget = event.relatedTarget; + + if (!relatedTarget || relatedTarget.classList.contains("preview-expression")) { + return true; + } // $FlowIgnore + + + const inPreviewSelection = document.elementsFromPoint(event.clientX, event.clientY).some(el => el.classList.contains("preview-selection")); + return inPreviewSelection; +} + class Popup extends _react.Component { constructor(...args) { var _temp; @@ -70,11 +80,13 @@ class Popup extends _react.Component { return _temp = super(...args), this.onMouseLeave = e => { const relatedTarget = e.relatedTarget; - if (relatedTarget && relatedTarget.classList && (relatedTarget.classList.contains("popover") || relatedTarget.classList.contains("debug-expression") || relatedTarget.classList.contains("editor-mount"))) { - return; + if (!relatedTarget) { + return this.props.onClose(); } - this.props.onClose(); + if (!inPreview(e)) { + this.props.onClose(); + } }, _temp; } @@ -96,26 +108,6 @@ class Popup extends _react.Component { } } - componentDidMount() { - const { - value, - editor, - range - } = this.props; - - if (!value || !value.type == "object") { - return; - } - - this.marker = (0, _editor.markText)(editor, "preview-selection", range); - } - - componentWillUnmount() { - if (this.marker) { - this.marker.clear(); - } - } - getRoot() { const { expression, diff --git a/devtools/client/debugger/new/src/components/Editor/Preview/index.js b/devtools/client/debugger/new/src/components/Editor/Preview/index.js index e37029d5e75a..72299c6a6c7c 100644 --- a/devtools/client/debugger/new/src/components/Editor/Preview/index.js +++ b/devtools/client/debugger/new/src/components/Editor/Preview/index.js @@ -27,15 +27,40 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de /* 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 . */ +function inPopup(e) { + const { + relatedTarget + } = e; + + if (!relatedTarget) { + return true; + } + + const pop = relatedTarget.closest(".popover") || relatedTarget.classList.contains("debug-expression"); + return pop; +} + +function getElementFromPos(pos) { + // $FlowIgnore + return document.elementFromPoint(pos.x + pos.width / 2, pos.y + pos.height / 2); +} + class Preview extends _react.PureComponent { constructor(props) { super(props); + this.target = null; - this.onMouseOver = e => { - const { - target - } = e; - this.props.updatePreview(target, this.props.editor); + this.onTokenEnter = ({ + target, + tokenPos + }) => { + this.props.updatePreview(target, tokenPos, this.props.editor.codeMirror); + }; + + this.onTokenLeave = e => { + if (!inPopup(e)) { + this.props.clearPreview(); + } }; this.onMouseUp = () => { @@ -52,17 +77,11 @@ class Preview extends _react.PureComponent { return true; }; - this.onMouseLeave = e => { - const target = e.target; - - if (target.classList.contains("CodeMirror")) { - return; - } - + this.onScroll = () => { this.props.clearPreview(); }; - this.onClose = () => { + this.onClose = e => { this.props.clearPreview(); }; @@ -72,30 +91,54 @@ class Preview extends _react.PureComponent { } componentDidMount() { + this.updateListeners(); + } + + componentDidUpdate(prevProps) { + this.updateListeners(prevProps); + this.updateHighlight(prevProps); + } + + updateListeners(prevProps) { + const { + isPaused + } = this.props; const { codeMirror } = this.props.editor; const codeMirrorWrapper = codeMirror.getWrapperElement(); - codeMirrorWrapper.addEventListener("mouseover", this.onMouseOver); - codeMirrorWrapper.addEventListener("mouseup", this.onMouseUp); - codeMirrorWrapper.addEventListener("mousedown", this.onMouseDown); - codeMirrorWrapper.addEventListener("mouseleave", this.onMouseLeave); + const wasNotPaused = !prevProps || !prevProps.isPaused; + const wasPaused = prevProps && prevProps.isPaused; - if (document.body) { - document.body.addEventListener("mouseleave", this.onMouseLeave); + if (isPaused && wasNotPaused) { + codeMirror.on("scroll", this.onScroll); + codeMirror.on("tokenenter", this.onTokenEnter); + codeMirror.on("tokenleave", this.onTokenLeave); + codeMirrorWrapper.addEventListener("mouseup", this.onMouseUp); + codeMirrorWrapper.addEventListener("mousedown", this.onMouseDown); + } + + if (!isPaused && wasPaused) { + codeMirror.off("tokenenter", this.onTokenEnter); + codeMirror.off("tokenleave", this.onTokenLeave); + codeMirrorWrapper.removeEventListener("mouseup", this.onMouseUp); + codeMirrorWrapper.removeEventListener("mousedown", this.onMouseDown); } } - componentWillUnmount() { - const codeMirror = this.props.editor.codeMirror; - const codeMirrorWrapper = codeMirror.getWrapperElement(); - codeMirrorWrapper.removeEventListener("mouseover", this.onMouseOver); - codeMirrorWrapper.removeEventListener("mouseup", this.onMouseUp); - codeMirrorWrapper.removeEventListener("mousedown", this.onMouseDown); - codeMirrorWrapper.removeEventListener("mouseleave", this.onMouseLeave); + updateHighlight(prevProps) { + const { + preview + } = this.props; - if (document.body) { - document.body.removeEventListener("mouseleave", this.onMouseLeave); + if (preview && !preview.updating) { + const target = getElementFromPos(preview.cursorPos); + target && target.classList.add("preview-selection"); + } + + if (prevProps.preview && !prevProps.preview.updating) { + const target = getElementFromPos(prevProps.preview.cursorPos); + target && target.classList.remove("preview-selection"); } } @@ -143,6 +186,7 @@ class Preview extends _react.PureComponent { const mapStateToProps = state => ({ preview: (0, _selectors.getPreview)(state), + isPaused: (0, _selectors.getIsPaused)(state), selectedSource: (0, _selectors.getSelectedSource)(state) }); diff --git a/devtools/client/debugger/new/src/components/Editor/index.js b/devtools/client/debugger/new/src/components/Editor/index.js index 4ff680a9ef92..7eaf8727cc1c 100644 --- a/devtools/client/debugger/new/src/components/Editor/index.js +++ b/devtools/client/debugger/new/src/components/Editor/index.js @@ -261,6 +261,7 @@ class Editor extends _react.PureComponent { codeMirrorWrapper.tabIndex = 0; codeMirrorWrapper.addEventListener("keydown", e => this.onKeyDown(e)); codeMirrorWrapper.addEventListener("click", e => this.onClick(e)); + codeMirrorWrapper.addEventListener("mouseover", (0, _editor.onMouseOver)(codeMirror)); const toggleFoldMarkerVisibility = e => { if (node instanceof HTMLElement) { diff --git a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/Breakpoint.js b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/Breakpoint.js index 9f23d40030b9..5c972b2a3c38 100644 --- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/Breakpoint.js +++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/Breakpoint.js @@ -152,6 +152,8 @@ class Breakpoint extends _react.PureComponent { __html: node.innerHTML }; } + /* eslint-disable react/no-danger */ + render() { const { diff --git a/devtools/client/debugger/new/src/components/shared/Popover.js b/devtools/client/debugger/new/src/components/shared/Popover.js index 1d9ee2c2cab9..a5bb7ad61c93 100644 --- a/devtools/client/debugger/new/src/components/shared/Popover.js +++ b/devtools/client/debugger/new/src/components/shared/Popover.js @@ -57,18 +57,6 @@ class Popover extends _react.Component { return this.calculateTopForRightOrientation(target, editor, popover); }; - this.onMouseLeave = e => { - const { - onMouseLeave - } = this.props; - - if (/^(bracket-arrow|gap)$/.test(e.currentTarget.className)) { - return; - } - - onMouseLeave(e); - }; - this.state = { left: 0, top: 0, @@ -260,7 +248,7 @@ class Popover extends _react.Component { className: (0, _classnames2.default)("popover", `orientation-${orientation}`, { up: orientation === "up" }), - onMouseLeave: this.onMouseLeave, + onMouseLeave: this.props.onMouseLeave, style: { top, left diff --git a/devtools/client/debugger/new/src/reducers/ast.js b/devtools/client/debugger/new/src/reducers/ast.js index 9bf70204dfb7..f3b502b8a22d 100644 --- a/devtools/client/debugger/new/src/reducers/ast.js +++ b/devtools/client/debugger/new/src/reducers/ast.js @@ -103,11 +103,16 @@ function update(state = initialASTState(), action) { if (!action.value) { return state.set("preview", null); + } // NOTE: if the preview does not exist, it has been cleared + + + if (state.get("preview")) { + return state.set("preview", _objectSpread({}, action.value, { + updating: false + })); } - return state.set("preview", _objectSpread({}, action.value, { - updating: false - })); + return state; } case "RESUME": diff --git a/devtools/client/debugger/new/src/reducers/pause.js b/devtools/client/debugger/new/src/reducers/pause.js index 5c2a2e11a7be..e53cde05a6b1 100644 --- a/devtools/client/debugger/new/src/reducers/pause.js +++ b/devtools/client/debugger/new/src/reducers/pause.js @@ -8,6 +8,7 @@ exports.getPauseReason = getPauseReason; exports.getPauseCommand = getPauseCommand; exports.isStepping = isStepping; exports.isPaused = isPaused; +exports.getIsPaused = getIsPaused; exports.getPreviousPauseFrameLocation = getPreviousPauseFrameLocation; exports.isEvaluatingExpression = isEvaluatingExpression; exports.getPopupObjectProperties = getPopupObjectProperties; @@ -304,6 +305,10 @@ function isPaused(state) { return !!getFrames(state); } +function getIsPaused(state) { + return !!getFrames(state); +} + function getPreviousPauseFrameLocation(state) { return state.pause.previousLocation; } diff --git a/devtools/client/debugger/new/src/utils/editor/index.js b/devtools/client/debugger/new/src/utils/editor/index.js index f56f27ec4237..d0a61fa57371 100644 --- a/devtools/client/debugger/new/src/utils/editor/index.js +++ b/devtools/client/debugger/new/src/utils/editor/index.js @@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); +exports.onMouseOver = undefined; var _sourceDocuments = require("./source-documents"); @@ -51,6 +52,15 @@ Object.keys(_ui).forEach(function (key) { } }); }); + +var _tokenEvents = require("./token-events"); + +Object.defineProperty(exports, "onMouseOver", { + enumerable: true, + get: function () { + return _tokenEvents.onMouseOver; + } +}); exports.getEditor = getEditor; exports.removeEditor = removeEditor; exports.shouldShowPrettyPrint = shouldShowPrettyPrint; diff --git a/devtools/client/debugger/new/src/utils/editor/moz.build b/devtools/client/debugger/new/src/utils/editor/moz.build index 5197e883430f..0910fd9f41af 100644 --- a/devtools/client/debugger/new/src/utils/editor/moz.build +++ b/devtools/client/debugger/new/src/utils/editor/moz.build @@ -15,4 +15,5 @@ DevToolsModules( 'source-documents.js', 'source-editor.js', 'source-search.js', + 'token-events.js', ) diff --git a/devtools/client/debugger/new/src/utils/editor/token-events.js b/devtools/client/debugger/new/src/utils/editor/token-events.js new file mode 100644 index 000000000000..1a6d1987a132 --- /dev/null +++ b/devtools/client/debugger/new/src/utils/editor/token-events.js @@ -0,0 +1,93 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.onMouseOver = onMouseOver; + +var _ = require("./index"); + +var _lodash = require("devtools/client/shared/vendor/lodash"); + +/* 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 . */ +function isInvalidTarget(target) { + if (!target || !target.innerText) { + return true; + } + + const tokenText = target.innerText.trim(); + const cursorPos = target.getBoundingClientRect(); // exclude literal tokens where it does not make sense to show a preview + + const invalidType = ["cm-atom", ""].includes(target.className); // exclude syntax where the expression would be a syntax error + + const invalidToken = tokenText === "" || tokenText.match(/^[(){}\|&%,.;=<>\+-/\*\s](?=)/); // exclude codemirror elements that are not tokens + + const invalidTarget = target.parentElement && !target.parentElement.closest(".CodeMirror-line") || cursorPos.top == 0; + const invalidClasses = ["editor-mount"]; + + if (invalidClasses.some(className => target.classList.contains(className))) { + return true; + } + + if (target.closest(".popover")) { + return true; + } + + return invalidTarget || invalidToken || invalidType; +} + +function dispatch(codeMirror, eventName, data) { + codeMirror.constructor.signal(codeMirror, eventName, data); +} + +function invalidLeaveTarget(target) { + if (!target || target.closest(".popover")) { + return true; + } + + return false; +} + +function onMouseOver(codeMirror) { + let prevTokenPos = null; + + function onMouseLeave(event) { + if (invalidLeaveTarget(event.relatedTarget)) { + return addMouseLeave(event.target); + } + + prevTokenPos = null; + dispatch(codeMirror, "tokenleave", event); + } + + function addMouseLeave(target) { + target.addEventListener("mouseleave", onMouseLeave, { + capture: true, + once: true + }); + } + + return enterEvent => { + const { + target + } = enterEvent; + + if (isInvalidTarget(target)) { + return; + } + + const tokenPos = (0, _.getTokenLocation)(codeMirror, target); + + if (!(0, _lodash.isEqual)(prevTokenPos, tokenPos)) { + addMouseLeave(target); + dispatch(codeMirror, "tokenenter", { + event: enterEvent, + target, + tokenPos + }); + prevTokenPos = tokenPos; + } + }; +} \ No newline at end of file diff --git a/devtools/client/debugger/new/test/mochitest/browser.ini b/devtools/client/debugger/new/test/mochitest/browser.ini index 9affd8b99d43..240e1d96f94d 100644 --- a/devtools/client/debugger/new/test/mochitest/browser.ini +++ b/devtools/client/debugger/new/test/mochitest/browser.ini @@ -158,7 +158,7 @@ skip-if = (os == "win" && ccov) # Bug 1453549 skip-if = ccov || (verify && debug && (os == 'linux')) # Bug 1441545 [browser_dbg-sourcemapped-stepping.js] [browser_dbg-sourcemapped-preview.js] -skip-if = (os == "win" && ccov) || (os == "win" && !debug) # Bug 1448523, Bug 1448450 +skip-if = os == "win" # Bug 1448523, Bug 1448450 [browser_dbg-breaking.js] [browser_dbg-breaking-from-console.js] [browser_dbg-breakpoints.js] diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg-pause-points.js b/devtools/client/debugger/new/test/mochitest/browser_dbg-pause-points.js index 1817f758daac..10f5a4088197 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg-pause-points.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-pause-points.js @@ -32,6 +32,7 @@ async function testCase(dbg, { name, count, steps }) { add_task(async function test() { const dbg = await initDebugger("doc-pause-points.html"); + await selectSource(dbg, "pause-points.js") await testCase(dbg, { name: "statements", count: 7, diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg-preview-module.js b/devtools/client/debugger/new/test/mochitest/browser_dbg-preview-module.js index 81cc9b245b58..365639c78599 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg-preview-module.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-preview-module.js @@ -5,10 +5,7 @@ // and doesn't have functions. add_task(async function() { const dbg = await initDebugger("doc-scripts.html"); - const { - selectors: { getSelectedSource }, - getState - } = dbg; + const { selectors: { getSelectedSource }, getState } = dbg; navigate(dbg, "doc-on-load.html"); diff --git a/devtools/client/debugger/new/test/mochitest/helpers.js b/devtools/client/debugger/new/test/mochitest/helpers.js index d1146f2581c0..56941f1af947 100644 --- a/devtools/client/debugger/new/test/mochitest/helpers.js +++ b/devtools/client/debugger/new/test/mochitest/helpers.js @@ -1213,18 +1213,31 @@ function getCoordsFromPosition(cm, { line, ch }) { return cm.charCoords({ line: ~~line, ch: ~~ch }); } -function hoverAtPos(dbg, { line, ch }) { +async function waitForScrolling(codeMirror) { + return new Promise(resolve => { + codeMirror.on("scroll", resolve); + setTimeout(resolve, 500); + }) +} + + +async function hoverAtPos(dbg, { line, ch }) { info(`Hovering at ${line}, ${ch}`); const cm = getCM(dbg); // Ensure the line is visible with margin because the bar at the bottom of // the editor overlaps into what the editor things is its own space, blocking // the click event below. - cm.scrollIntoView({ line: line - 1, ch }, 100); + cm.scrollIntoView({ line: line - 1, ch }, 0); + await waitForScrolling(cm); const coords = getCoordsFromPosition(cm, { line: line - 1, ch }); const tokenEl = dbg.win.document.elementFromPoint(coords.left, coords.top); + if (!tokenEl) { + return false; + } + tokenEl.dispatchEvent( new MouseEvent("mouseover", { bubbles: true, @@ -1234,6 +1247,9 @@ function hoverAtPos(dbg, { line, ch }) { ); } +// tryHovering will hover at a position every second until we +// see a preview element (popup, tooltip) appear. Once it appears, +// it considers it a success. function tryHovering(dbg, line, column, elementName) { return new Promise((resolve, reject) => { const element = waitForElement(dbg, elementName); @@ -1251,7 +1267,7 @@ function tryHovering(dbg, line, column, elementName) { } hoverAtPos(dbg, { line, ch: column - 1 }); - }, 200); + }, 1000); }); } diff --git a/devtools/server/actors/thread.js b/devtools/server/actors/thread.js index 5dafc2bfd907..e11cb9bec19a 100644 --- a/devtools/server/actors/thread.js +++ b/devtools/server/actors/thread.js @@ -536,6 +536,7 @@ const ThreadActor = ActorClassWithSpec(threadSpec, { // When pause points are specified for the source, // we should pause when we are at a stepOver pause point const pausePoint = findPausePointForLocation(pausePoints, newLocation); + if (pausePoint) { if (pausePoint.step) { return pauseAndRespond(this); diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp index 2bf26d0a4c11..673d1c537993 100644 --- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp @@ -1313,18 +1313,6 @@ ReparentWrappersInSubtree(nsIContent* aRoot) JSContext* cx = jsapi.cx(); - nsIGlobalObject* docGlobal = aRoot->OwnerDoc()->GetScopeObject(); - if (NS_WARN_IF(!docGlobal)) { - return NS_ERROR_UNEXPECTED; - } - - JS::Rooted rootedGlobal(cx, docGlobal->GetGlobalJSObject()); - if (NS_WARN_IF(!rootedGlobal)) { - return NS_ERROR_UNEXPECTED; - } - - rootedGlobal = xpc::GetXBLScope(cx, rootedGlobal); - ErrorResult rv; JS::Rooted reflector(cx); for (nsIContent* cur = aRoot; cur; cur = cur->GetNextNode(aRoot)) { diff --git a/dom/clients/manager/ClientManagerService.cpp b/dom/clients/manager/ClientManagerService.cpp index 6af08849c111..c6f20464890b 100644 --- a/dom/clients/manager/ClientManagerService.cpp +++ b/dom/clients/manager/ClientManagerService.cpp @@ -475,6 +475,8 @@ ClaimOnMainThread(const ClientInfo& aClientInfo, }, [promise] (nsresult aRv) { promise->Reject(aRv, __func__); }); + + scopeExit.release(); }); MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget())); diff --git a/dom/clients/manager/ClientSource.cpp b/dom/clients/manager/ClientSource.cpp index 3bbc417b9136..5bb18b7719bf 100644 --- a/dom/clients/manager/ClientSource.cpp +++ b/dom/clients/manager/ClientSource.cpp @@ -124,6 +124,28 @@ ClientSource::GetDocShell() const return mOwner.as>(); } +nsIGlobalObject* +ClientSource::GetGlobal() const +{ + NS_ASSERT_OWNINGTHREAD(ClientSource); + nsPIDOMWindowInner* win = GetInnerWindow(); + if (win) { + return win->AsGlobal(); + } + + WorkerPrivate* wp = GetWorkerPrivate(); + if (wp) { + return wp->GlobalScope(); + } + + // Note, ClientSource objects attached to docshell for conceptual + // initial about:blank will get nullptr here. The caller should + // use MaybeCreateIntitialDocument() to create the window before + // GetGlobal() if it wants this before. + + return nullptr; +} + void ClientSource::MaybeCreateInitialDocument() { @@ -431,10 +453,47 @@ ClientSource::Control(const ClientControlledArgs& aArgs) { NS_ASSERT_OWNINGTHREAD(ClientSource); + // Determine if the client is allowed to be controlled. Currently we + // prevent service workers from controlling clients that cannot access + // storage. We exempt this restriction for local URL clients, like about:blank + // and blob:, since access to service workers is dictated by their parent. + // + // Note, we default to allowing the client to be controlled in the case + // where we are not execution ready yet. This can only happen if the + // the non-subresource load is intercepted by a service worker. Since + // ServiceWorkerInterceptController() uses StorageAllowedForChannel() + // it should be fine to accept these control messages. + // + // Its also fine to default to allowing ClientSource attached to a docshell + // to be controlled. These clients represent inital about:blank windows + // that do not have an inner window created yet. We explicitly allow initial + // about:blank. + bool controlAllowed = true; + if (GetInnerWindow()) { + + // Local URL windows and windows with access to storage can be controlled. + controlAllowed = Info().URL().LowerCaseEqualsLiteral("about:blank") || + StringBeginsWith(Info().URL(), NS_LITERAL_CSTRING("blob:")) || + nsContentUtils::StorageAllowedForWindow(GetInnerWindow()) == + nsContentUtils::StorageAccess::eAllow; + } else if (GetWorkerPrivate()) { + // Local URL workers and workers with access to storage cna be controlled. + controlAllowed = GetWorkerPrivate()->IsStorageAllowed() || + StringBeginsWith(GetWorkerPrivate()->ScriptURL(), + NS_LITERAL_STRING("blob:")); + } + + RefPtr ref; + + if (NS_WARN_IF(!controlAllowed)) { + ref = ClientOpPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR, + __func__); + return ref.forget(); + } + SetController(ServiceWorkerDescriptor(aArgs.serviceWorker())); - RefPtr ref = - ClientOpPromise::CreateAndResolve(NS_OK, __func__); + ref = ClientOpPromise::CreateAndResolve(NS_OK, __func__); return ref.forget(); } @@ -670,35 +729,55 @@ ClientSource::PostMessage(const ClientPostMessageArgs& aArgs) RefPtr ClientSource::Claim(const ClientClaimArgs& aArgs) { + // The ClientSource::Claim method is only needed in the legacy + // mode where the ServiceWorkerManager is run in each child-process. + // In parent-process mode this method should not be called. + MOZ_DIAGNOSTIC_ASSERT(!ServiceWorkerParentInterceptEnabled()); + RefPtr ref; + nsIGlobalObject* global = GetGlobal(); + if (NS_WARN_IF(!global)) { + ref = ClientOpPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR, + __func__); + return ref.forget(); + } + + // Note, we cannot just mark the ClientSource controlled. We must go through + // the SWM so that it can keep track of which clients are controlled by each + // registration. We must tell the child-process SWM in legacy child-process + // mode. In parent-process service worker mode the SWM is notified in the + // parent-process in ClientManagerService::Claim(). + + RefPtr innerPromise = + new GenericPromise::Private(__func__); ServiceWorkerDescriptor swd(aArgs.serviceWorker()); - // Today the ServiceWorkerManager maintains its own list of - // nsIDocument objects controlled by each service worker. We - // need to try to update that data structure for now. If we - // can't, however, then simply mark the Client as controlled. - // In the future this will be enough for the SWM as well since - // it will eventually hold ClientHandle objects instead of - // nsIDocuments. - nsPIDOMWindowInner* innerWindow = GetInnerWindow(); - nsIDocument* doc = innerWindow ? innerWindow->GetExtantDoc() : nullptr; - RefPtr swm = doc ? ServiceWorkerManager::GetInstance() - : nullptr; - if (!swm || !doc) { - SetController(swd); - ref = ClientOpPromise::CreateAndResolve(NS_OK, __func__); - return ref.forget(); + nsCOMPtr r = NS_NewRunnableFunction( + "ClientSource::Claim", + [innerPromise, clientInfo = mClientInfo, swd] () mutable { + RefPtr swm = ServiceWorkerManager::GetInstance(); + if (NS_WARN_IF(!swm)) { + innerPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__); + return; + } + + RefPtr p = swm->MaybeClaimClient(clientInfo, swd); + p->ChainTo(innerPromise.forget(), __func__); + }); + + if (NS_IsMainThread()) { + r->Run(); + } else { + MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget())); } RefPtr outerPromise = new ClientOpPromise::Private(__func__); - auto holder = - MakeRefPtr>(innerWindow->AsGlobal()); + auto holder = MakeRefPtr>(global); - RefPtr p = swm->MaybeClaimClient(mClientInfo, swd); - p->Then(mEventTarget, __func__, + innerPromise->Then(mEventTarget, __func__, [outerPromise, holder] (bool aResult) { holder->Complete(); outerPromise->Resolve(NS_OK, __func__); diff --git a/dom/clients/manager/ClientSource.h b/dom/clients/manager/ClientSource.h index 671f40ccae83..471945194d00 100644 --- a/dom/clients/manager/ClientSource.h +++ b/dom/clients/manager/ClientSource.h @@ -17,6 +17,7 @@ #endif class nsIDocShell; +class nsIGlobalObject; class nsISerialEventTarget; class nsPIDOMWindowInner; @@ -78,6 +79,9 @@ class ClientSource final : public ClientThing nsIDocShell* GetDocShell() const; + nsIGlobalObject* + GetGlobal() const; + void MaybeCreateInitialDocument(); diff --git a/dom/clients/manager/ClientSourceOpParent.cpp b/dom/clients/manager/ClientSourceOpParent.cpp index 9dfb2cd1d367..67d5dcdec81c 100644 --- a/dom/clients/manager/ClientSourceOpParent.cpp +++ b/dom/clients/manager/ClientSourceOpParent.cpp @@ -6,6 +6,8 @@ #include "ClientSourceOpParent.h" +#include "ClientSourceParent.h" + namespace mozilla { namespace dom { @@ -25,10 +27,22 @@ ClientSourceOpParent::Recv__delete__(const ClientOpResult& aResult) { if (aResult.type() == ClientOpResult::Tnsresult && NS_FAILED(aResult.get_nsresult())) { + + // If a control message fails then clear the controller from + // the ClientSourceParent. We eagerly marked it controlled at + // the start of the operation. + if (mArgs.type() == ClientOpConstructorArgs::TClientControlledArgs) { + auto source = static_cast(Manager()); + if (source) { + source->ClearController(); + } + } + mPromise->Reject(aResult.get_nsresult(), __func__); mPromise = nullptr; return IPC_OK(); } + mPromise->Resolve(aResult, __func__); mPromise = nullptr; return IPC_OK(); @@ -36,7 +50,8 @@ ClientSourceOpParent::Recv__delete__(const ClientOpResult& aResult) ClientSourceOpParent::ClientSourceOpParent(const ClientOpConstructorArgs& aArgs, ClientOpPromise::Private* aPromise) - : mPromise(aPromise) + : mArgs(aArgs) + , mPromise(aPromise) { MOZ_DIAGNOSTIC_ASSERT(mPromise); } diff --git a/dom/clients/manager/ClientSourceOpParent.h b/dom/clients/manager/ClientSourceOpParent.h index 7ebd9d24ce11..356b041c9076 100644 --- a/dom/clients/manager/ClientSourceOpParent.h +++ b/dom/clients/manager/ClientSourceOpParent.h @@ -14,6 +14,7 @@ namespace dom { class ClientSourceOpParent final : public PClientSourceOpParent { + const ClientOpConstructorArgs mArgs; RefPtr mPromise; // PClientSourceOpParent interface diff --git a/dom/clients/manager/ClientSourceParent.cpp b/dom/clients/manager/ClientSourceParent.cpp index a46f5ba49121..5818108e3e6d 100644 --- a/dom/clients/manager/ClientSourceParent.cpp +++ b/dom/clients/manager/ClientSourceParent.cpp @@ -274,6 +274,12 @@ ClientSourceParent::GetController() const return mController; } +void +ClientSourceParent::ClearController() +{ + mController.reset(); +} + void ClientSourceParent::AttachHandle(ClientHandleParent* aClientHandle) { @@ -298,7 +304,11 @@ ClientSourceParent::StartOp(const ClientOpConstructorArgs& aArgs) new ClientOpPromise::Private(__func__); // If we are being controlled, remember that data before propagating - // on to the ClientSource. + // on to the ClientSource. This must be set prior to triggering + // the controllerchange event from the ClientSource since some tests + // expect matchAll() to find the controlled client immediately after. + // If the control operation fails, then we reset the controller value + // to reflect the final state. if (aArgs.type() == ClientOpConstructorArgs::TClientControlledArgs) { mController.reset(); mController.emplace(aArgs.get_ClientControlledArgs().serviceWorker()); diff --git a/dom/clients/manager/ClientSourceParent.h b/dom/clients/manager/ClientSourceParent.h index d618d5ee5cda..b1fc26d806e9 100644 --- a/dom/clients/manager/ClientSourceParent.h +++ b/dom/clients/manager/ClientSourceParent.h @@ -79,6 +79,9 @@ public: const Maybe& GetController() const; + void + ClearController(); + void AttachHandle(ClientHandleParent* aClientSource); diff --git a/dom/serviceworkers/ServiceWorkerManager.cpp b/dom/serviceworkers/ServiceWorkerManager.cpp index 96f092949ba3..72e0a9d84715 100644 --- a/dom/serviceworkers/ServiceWorkerManager.cpp +++ b/dom/serviceworkers/ServiceWorkerManager.cpp @@ -317,6 +317,7 @@ ServiceWorkerManager::StartControllingClient(const ClientInfo& aClientInfo, MOZ_DIAGNOSTIC_ASSERT(aRegistrationInfo->GetActive()); RefPtr ref; + RefPtr self(this); const ServiceWorkerDescriptor& active = aRegistrationInfo->GetActive()->Descriptor(); @@ -326,7 +327,12 @@ ServiceWorkerManager::StartControllingClient(const ClientInfo& aClientInfo, RefPtr old = entry.Data()->mRegistrationInfo.forget(); - ref = entry.Data()->mClientHandle->Control(active); + if (aControlClientHandle) { + ref = entry.Data()->mClientHandle->Control(active); + } else { + ref = GenericPromise::CreateAndResolve(false, __func__); + } + entry.Data()->mRegistrationInfo = aRegistrationInfo; if (old != aRegistrationInfo) { @@ -336,6 +342,17 @@ ServiceWorkerManager::StartControllingClient(const ClientInfo& aClientInfo, Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1); + // Always check to see if we failed to actually control the client. In + // that case removed the client from our list of controlled clients. + ref->Then( + SystemGroup::EventTargetFor(TaskCategory::Other), __func__, + [] (bool) { + // do nothing on success + }, [self, aClientInfo] (nsresult aRv) { + // failed to control, forget about this client + self->StopControllingClient(aClientInfo); + }); + return ref; } @@ -355,15 +372,25 @@ ServiceWorkerManager::StartControllingClient(const ClientInfo& aClientInfo, return new ControlledClientData(clientHandle, aRegistrationInfo); }); - RefPtr self(this); clientHandle->OnDetach()->Then( SystemGroup::EventTargetFor(TaskCategory::Other), __func__, - [self = std::move(self), aClientInfo] { + [self, aClientInfo] { self->StopControllingClient(aClientInfo); }); Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1); + // Always check to see if we failed to actually control the client. In + // that case removed the client from our list of controlled clients. + ref->Then( + SystemGroup::EventTargetFor(TaskCategory::Other), __func__, + [] (bool) { + // do nothing on success + }, [self, aClientInfo] (nsresult aRv) { + // failed to control, forget about this client + self->StopControllingClient(aClientInfo); + }); + return ref; } @@ -2695,7 +2722,20 @@ ServiceWorkerManager::UpdateClientControllers(ServiceWorkerRegistrationInfo* aRe // Fire event after iterating mControlledClients is done to prevent // modification by reentering from the event handlers during iteration. for (auto& handle : handleList) { - handle->Control(activeWorker->Descriptor()); + RefPtr p = handle->Control(activeWorker->Descriptor()); + + RefPtr self = this; + + // If we fail to control the client, then automatically remove it + // from our list of controlled clients. + p->Then( + SystemGroup::EventTargetFor(TaskCategory::Other), __func__, + [] (bool) { + // do nothing on success + }, [self, clientInfo = handle->Info()] (nsresult aRv) { + // failed to control, forget about this client + self->StopControllingClient(clientInfo); + }); } } diff --git a/dom/websocket/WebSocket.cpp b/dom/websocket/WebSocket.cpp index 8d81615da18e..fe4a4b4666cc 100644 --- a/dom/websocket/WebSocket.cpp +++ b/dom/websocket/WebSocket.cpp @@ -820,6 +820,11 @@ WebSocketImpl::ScheduleConnectionCloseEvents(nsISupports* aContext, aStatusCode = NS_OK; } + if (aStatusCode == NS_ERROR_NET_INADEQUATE_SECURITY) { + // TLS negotiation failed so we need to set status code to 1015. + mCloseEventCode = 1015; + } + if (NS_FAILED(aStatusCode)) { ConsoleError(); mFailed = true; diff --git a/gfx/layers/wr/WebRenderLayerManager.cpp b/gfx/layers/wr/WebRenderLayerManager.cpp index 0103476c893f..29bcbb62636e 100644 --- a/gfx/layers/wr/WebRenderLayerManager.cpp +++ b/gfx/layers/wr/WebRenderLayerManager.cpp @@ -240,16 +240,15 @@ WebRenderLayerManager::EndTransaction(DrawPaintedLayerCallback aCallback, void WebRenderLayerManager::EndTransactionWithoutLayer(nsDisplayList* aDisplayList, nsDisplayListBuilder* aDisplayListBuilder, - const nsTArray& aFilters) + const nsTArray& aFilters, + WebRenderBackgroundData* aBackground) { - MOZ_ASSERT(aDisplayList && aDisplayListBuilder); - AUTO_PROFILER_TRACING("Paint", "RenderLayers"); #if DUMP_LISTS // Useful for debugging, it dumps the display list *before* we try to build // WR commands from it - if (XRE_IsContentProcess()) nsFrame::PrintDisplayList(aDisplayListBuilder, *aDisplayList); + if (XRE_IsContentProcess() && aDisplayList) nsFrame::PrintDisplayList(aDisplayListBuilder, *aDisplayList); #endif #ifdef XP_WIN @@ -266,7 +265,9 @@ WebRenderLayerManager::EndTransactionWithoutLayer(nsDisplayList* aDisplayList, wr::DisplayListBuilder builder(WrBridge()->GetPipeline(), contentSize, mLastDisplayListSize); wr::IpcResourceUpdateQueue resourceUpdates(WrBridge()); - { // Record the time spent "layerizing". WR doesn't actually layerize but + if (aDisplayList) { + MOZ_ASSERT(aDisplayListBuilder && !aBackground); + // Record the time spent "layerizing". WR doesn't actually layerize but // generating the WR display list is the closest equivalent PaintTelemetry::AutoRecord record(PaintTelemetry::Metric::Layerization); @@ -277,6 +278,10 @@ WebRenderLayerManager::EndTransactionWithoutLayer(nsDisplayList* aDisplayList, mScrollData, contentSize, aFilters); + } else { + // ViewToPaint does not have frame yet, then render only background clolor. + MOZ_ASSERT(!aDisplayListBuilder && aBackground); + aBackground->AddWebRenderCommands(builder); } DiscardCompositorAnimations(); diff --git a/gfx/layers/wr/WebRenderLayerManager.h b/gfx/layers/wr/WebRenderLayerManager.h index bba7f02fc825..f7e1f4be3bf8 100644 --- a/gfx/layers/wr/WebRenderLayerManager.h +++ b/gfx/layers/wr/WebRenderLayerManager.h @@ -71,7 +71,8 @@ public: virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) override; void EndTransactionWithoutLayer(nsDisplayList* aDisplayList, nsDisplayListBuilder* aDisplayListBuilder, - const nsTArray& aFilters = nsTArray()); + const nsTArray& aFilters = nsTArray(), + WebRenderBackgroundData* aBackground = nullptr); virtual void EndTransaction(DrawPaintedLayerCallback aCallback, void* aCallbackData, EndTransactionFlags aFlags = END_DEFAULT) override; diff --git a/gfx/layers/wr/WebRenderUserData.cpp b/gfx/layers/wr/WebRenderUserData.cpp index 5204d2def528..cafca3521d6b 100644 --- a/gfx/layers/wr/WebRenderUserData.cpp +++ b/gfx/layers/wr/WebRenderUserData.cpp @@ -20,6 +20,15 @@ namespace mozilla { namespace layers { +void +WebRenderBackgroundData::AddWebRenderCommands(wr::DisplayListBuilder& aBuilder) +{ + aBuilder.PushRect(mBounds, + mBounds, + true, + mColor); +} + /* static */ bool WebRenderUserData::SupportsAsyncUpdate(nsIFrame* aFrame) { diff --git a/gfx/layers/wr/WebRenderUserData.h b/gfx/layers/wr/WebRenderUserData.h index baa5cf09cc8a..b69098370e13 100644 --- a/gfx/layers/wr/WebRenderUserData.h +++ b/gfx/layers/wr/WebRenderUserData.h @@ -37,6 +37,19 @@ class WebRenderFallbackData; class WebRenderLayerManager; class WebRenderGroupData; +class WebRenderBackgroundData +{ +public: + WebRenderBackgroundData(wr::LayoutRect aBounds, wr::ColorF aColor) + : mBounds(aBounds) + , mColor(aColor) + { } + void AddWebRenderCommands(wr::DisplayListBuilder& aBuilder); +protected: + wr::LayoutRect mBounds; + wr::ColorF mColor; +}; + class WebRenderUserData { public: diff --git a/gfx/webrender_bindings/RenderCompositorANGLE.cpp b/gfx/webrender_bindings/RenderCompositorANGLE.cpp index bec4d84fefac..c1a1cb070790 100644 --- a/gfx/webrender_bindings/RenderCompositorANGLE.cpp +++ b/gfx/webrender_bindings/RenderCompositorANGLE.cpp @@ -55,6 +55,10 @@ ID3D11Device* RenderCompositorANGLE::GetDeviceOfEGLDisplay() { auto* egl = gl::GLLibraryEGL::Get(); + MOZ_ASSERT(egl); + if (!egl || !egl->IsExtensionSupported(gl::GLLibraryEGL::EXT_device_query)) { + return nullptr; + } // Fetch the D3D11 device. EGLDeviceEXT eglDevice = nullptr; diff --git a/js/public/CallArgs.h b/js/public/CallArgs.h index e32d5c8fc7f3..8506346ac139 100644 --- a/js/public/CallArgs.h +++ b/js/public/CallArgs.h @@ -5,6 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* + * [SMDOC] JS::CallArgs API + * * Helper classes encapsulating access to the callee, |this| value, arguments, * and argument count for a call/construct operation. * @@ -46,10 +48,7 @@ * * It's possible (albeit deprecated) to manually index into |vp| to access the * callee, |this|, and arguments of a function, and to set its return value. - * It's also possible to use the supported API of JS_CALLEE, JS_THIS, JS_ARGV, - * JS_RVAL, and JS_SET_RVAL to the same ends. - * - * But neither API has the error-handling or moving-GC correctness of CallArgs. + * This does not have the error-handling or moving-GC correctness of CallArgs. * New code should use CallArgs instead whenever possible. * * The eventual plan is to change JSNative to take |const CallArgs&| directly, diff --git a/js/public/Debug.h b/js/public/Debug.h index 68db73a82cb3..0c8a47133c8c 100644 --- a/js/public/Debug.h +++ b/js/public/Debug.h @@ -27,6 +27,8 @@ class Debugger; namespace JS { namespace dbg { +// [SMDOC] Debugger builder API +// // Helping embedding code build objects for Debugger // ------------------------------------------------- // diff --git a/js/public/Proxy.h b/js/public/Proxy.h index 96eaa4d0f21a..647e65cdaac0 100644 --- a/js/public/Proxy.h +++ b/js/public/Proxy.h @@ -37,6 +37,8 @@ class RegExpShared; class JS_FRIEND_API(Wrapper); /* + * [SMDOC] Proxy Objects + * * A proxy is a JSObject with highly customizable behavior. ES6 specifies a * single kind of proxy, but the customization mechanisms we use to implement * ES6 Proxy objects are also useful wherever an object with weird behavior is diff --git a/js/public/Result.h b/js/public/Result.h index 346a1e6443d8..5d22c46a8839 100644 --- a/js/public/Result.h +++ b/js/public/Result.h @@ -5,6 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* + * [SMDOC] JS::Result + * * `Result` is used as the return type of many SpiderMonkey functions that * can either succeed or fail. See "/mfbt/Result.h". * diff --git a/js/public/RootingAPI.h b/js/public/RootingAPI.h index b0d138acd7c0..b1414e000f4c 100644 --- a/js/public/RootingAPI.h +++ b/js/public/RootingAPI.h @@ -28,6 +28,8 @@ #include "js/Utility.h" /* + * [SMDOC] Stack Rooting + * * Moving GC Stack Rooting * * A moving GC may change the physical location of GC allocated things, even diff --git a/js/public/UbiNode.h b/js/public/UbiNode.h index c02dcad3be36..a4f8d9cb8e9f 100644 --- a/js/public/UbiNode.h +++ b/js/public/UbiNode.h @@ -29,7 +29,7 @@ #include "js/Value.h" #include "js/Vector.h" -// JS::ubi::Node +// [SMDOC] ubi::Node (Heap Analysis framework) // // JS::ubi::Node is a pointer-like type designed for internal use by heap // analysis tools. A ubi::Node can refer to: diff --git a/js/public/Value.h b/js/public/Value.h index 20470b22ba07..325c393e7761 100644 --- a/js/public/Value.h +++ b/js/public/Value.h @@ -268,6 +268,8 @@ CanonicalizeNaN(double d) } /** + * [SMDOC] JS::Value type + * * JS::Value is the interface for a single JavaScript Engine value. A few * general notes on JS::Value: * diff --git a/js/src/builtin/Array.cpp b/js/src/builtin/Array.cpp index 74cc2c70658a..1b02e4b5556e 100644 --- a/js/src/builtin/Array.cpp +++ b/js/src/builtin/Array.cpp @@ -939,16 +939,6 @@ js::ArraySetLength(JSContext* cx, Handle arr, HandleId id, return result.succeed(); } -bool -js::WouldDefinePastNonwritableLength(HandleNativeObject obj, uint32_t index) -{ - if (!obj->is()) - return false; - - ArrayObject* arr = &obj->as(); - return !arr->lengthIsWritable() && index >= arr->length(); -} - static bool array_addProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v) { diff --git a/js/src/builtin/Array.h b/js/src/builtin/Array.h index 8a733c9e570b..1d110510a398 100644 --- a/js/src/builtin/Array.h +++ b/js/src/builtin/Array.h @@ -113,14 +113,6 @@ extern ArrayObject* NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length, HandleObject proto = nullptr); -/* - * Determines whether a write to the given element on |obj| should fail because - * |obj| is an Array with a non-writable length, and writing that element would - * increase the length of the array. - */ -extern bool -WouldDefinePastNonwritableLength(HandleNativeObject obj, uint32_t index); - extern bool GetLengthProperty(JSContext* cx, HandleObject obj, uint32_t* lengthp); diff --git a/js/src/builtin/TypedObject.h b/js/src/builtin/TypedObject.h index 9606fba3c866..7999b3a46c5c 100644 --- a/js/src/builtin/TypedObject.h +++ b/js/src/builtin/TypedObject.h @@ -18,7 +18,7 @@ /* * ------------- - * Typed Objects + * [SMDOC] Typed Objects * ------------- * * Typed objects are a special kind of JS object where the data is diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 5e62626dae50..000dfb465395 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -10,6 +10,8 @@ #define frontend_Parser_h /* + * [SMDOC] JS Parser + * * JS parsers capable of generating ASTs from source text. * * A parser embeds token stream information, then gets and matches tokens to diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index eebd79ec3b48..18b9cc583716 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -16,6 +16,8 @@ #define frontend_TokenStream_h /* + * [SMDOC] Parser Token Stream + * * A token stream exposes the raw tokens -- operators, names, numbers, * keywords, and so on -- of JavaScript source code. * diff --git a/js/src/gc/AtomMarking.cpp b/js/src/gc/AtomMarking.cpp index d20ab05cf67d..3f3c0bac3a66 100644 --- a/js/src/gc/AtomMarking.cpp +++ b/js/src/gc/AtomMarking.cpp @@ -15,7 +15,7 @@ namespace js { namespace gc { -// Atom Marking Overview +// [SMDOC] GC Atom Marking // // Things in the atoms zone (which includes atomized strings and other things, // all of which we will refer to as 'atoms' here) may be pointed to freely by diff --git a/js/src/gc/Barrier.h b/js/src/gc/Barrier.h index e3ac5797444a..ae279ce9981b 100644 --- a/js/src/gc/Barrier.h +++ b/js/src/gc/Barrier.h @@ -17,6 +17,8 @@ #include "js/Value.h" /* + * [SMDOC] GC Barriers + * * A write barrier is a mechanism used by incremental or generation GCs to * ensure that every value that needs to be marked is marked. In general, the * write barrier should be invoked whenever a write can cause the set of things diff --git a/js/src/gc/GC.cpp b/js/src/gc/GC.cpp index 2c6068a67c62..feea77754aa9 100644 --- a/js/src/gc/GC.cpp +++ b/js/src/gc/GC.cpp @@ -5,6 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* + * [SMDOC] Garbage Collector + * * This code implements an incremental mark-and-sweep garbage collector, with * most sweeping carried out in the background on a parallel thread. * diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index 17344b7e5167..b7d38bcfc64a 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -54,6 +54,8 @@ using mozilla::IsBaseOf; using mozilla::IsSame; using mozilla::PodCopy; +// [SMDOC] GC Tracing +// // Tracing Overview // ================ // diff --git a/js/src/gc/Scheduling.h b/js/src/gc/Scheduling.h index dc5ea3a9552f..b4aaab9ea3bd 100644 --- a/js/src/gc/Scheduling.h +++ b/js/src/gc/Scheduling.h @@ -5,6 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* + * [SMDOC] GC Scheduling + * * GC Scheduling Overview * ====================== * diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index ffc6ec6ee3af..853872dbb34b 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -99,6 +99,8 @@ class MOZ_NON_TEMPORARY_CLASS FunctionToStringCache namespace JS { +// [SMDOC] GC Zones +// // A zone is a collection of compartments. Every compartment belongs to exactly // one zone. In Firefox, there is roughly one zone per tab along with a system // zone for everything else. Zones mainly serve as boundaries for garbage diff --git a/js/src/irregexp/RegExpEngine.cpp b/js/src/irregexp/RegExpEngine.cpp index 0dc2be88ecf9..d381dc1e454e 100644 --- a/js/src/irregexp/RegExpEngine.cpp +++ b/js/src/irregexp/RegExpEngine.cpp @@ -1326,6 +1326,7 @@ Analysis::VisitAssertion(AssertionNode* that) EnsureAnalyzed(that->on_success()); } +// [SMDOC] Irregexp internals // ------------------------------------------------------------------- // Implementation of the Irregexp regular expression engine. // diff --git a/js/src/jit-test/tests/realms/switch-realms-native.js b/js/src/jit-test/tests/realms/switch-realms-native.js index 18599a4e64e7..e4ae74cffcd7 100644 --- a/js/src/jit-test/tests/realms/switch-realms-native.js +++ b/js/src/jit-test/tests/realms/switch-realms-native.js @@ -45,3 +45,16 @@ function testException1() { } } testException1(); + +function testDOMCalls() { + var g = newGlobal({sameCompartmentAs: this}); + var obj = g.evaluate("new FakeDOMObject()"); + for (var i = 0; i < 2000; i++) { + assertCorrectRealm(); + assertEq(obj.doFoo(1), 1); + assertEq(typeof obj.x, "number"); + assertEq(obj.global, g); + obj.global = g; // Throws if not setter's global. + } +} +testDOMCalls(); diff --git a/js/src/jit-test/tests/wasm/grow-memory.js b/js/src/jit-test/tests/wasm/grow-memory.js index 0b15cd08a688..d506dda7be50 100644 --- a/js/src/jit-test/tests/wasm/grow-memory.js +++ b/js/src/jit-test/tests/wasm/grow-memory.js @@ -1,11 +1,11 @@ -function linearModule(min, max, ops) { +function linearModule(min, max, ops, current_memory, grow_memory) { var opsText = ops.map(function (op) { if (op[0] == "CM") { - res = `(if i32 (i32.ne (current_memory) (i32.const ${op[1]})) + res = `(if i32 (i32.ne (${current_memory}) (i32.const ${op[1]})) (i32.load offset=10 (i32.const 4294967295)) (i32.const 0))` } else if (op[0] == "GM") { - res = `(if i32 (i32.ne (grow_memory (i32.const ${op[1]})) (i32.const ${op[2]})) + res = `(if i32 (i32.ne (${grow_memory} (i32.const ${op[1]})) (i32.const ${op[2]})) (i32.load offset=10 (i32.const 4294967295)) (i32.const 0))` } else if (op[0] == "L") { @@ -35,11 +35,12 @@ function linearModule(min, max, ops) { ` (func (result i32) (drop ` + opsText + `) - (current_memory) + (${current_memory}) ) (export "run" 0))`; return text; } // Just grow some memory -wasmFullPass(linearModule(3,5, [["CM", 3]]), 3); +wasmFullPass(linearModule(3,5, [["CM", 3]], "current_memory", "grow_memory"), 3); // Old opcode names +wasmFullPass(linearModule(3,5, [["CM", 3]], "memory.size", "memory.grow"), 3); // New opcode names diff --git a/js/src/jit/AliasAnalysis.cpp b/js/src/jit/AliasAnalysis.cpp index ebb13ac8502d..befd7e4f033e 100644 --- a/js/src/jit/AliasAnalysis.cpp +++ b/js/src/jit/AliasAnalysis.cpp @@ -116,6 +116,8 @@ IonSpewAliasInfo(const char* pre, MInstruction* ins, const char* post) #endif } +// [SMDOC] IonMonkey Alias Analysis +// // This pass annotates every load instruction with the last store instruction // on which it depends. The algorithm is optimistic in that it ignores explicit // dependencies and only considers loads and stores. diff --git a/js/src/jit/AtomicOperations.h b/js/src/jit/AtomicOperations.h index a8970b0d37e1..c645ecc3a4c6 100644 --- a/js/src/jit/AtomicOperations.h +++ b/js/src/jit/AtomicOperations.h @@ -15,6 +15,8 @@ namespace js { namespace jit { /* + * [SMDOC] Atomic Operations + * * The atomic operations layer defines types and functions for * JIT-compatible atomic operation. * diff --git a/js/src/jit/Bailouts.h b/js/src/jit/Bailouts.h index 1674a194cf00..dc5a3d1e56a8 100644 --- a/js/src/jit/Bailouts.h +++ b/js/src/jit/Bailouts.h @@ -16,8 +16,10 @@ namespace js { namespace jit { -// A "bailout" is a condition in which we need to recover an interpreter frame -// from an IonFrame. Bailouts can happen for the following reasons: +// [SMDOC] IonMonkey Bailouts +// +// A "bailout" is a condition in which we need to recover a baseline frame from +// an IonFrame. Bailouts can happen for the following reasons: // (1) A deoptimization guard, for example, an add overflows or a type check // fails. // (2) A check or assumption held by the JIT is invalidated by the VM, and @@ -36,7 +38,7 @@ namespace jit { // jmp _bailout // // The bailout target needs to somehow translate the Ion frame (whose state -// will differ at each program point) to an interpreter frame. This state is +// will differ at each program point) to a baseline frame. This state is // captured into the IonScript's snapshot buffer, and for each bailout we know // which snapshot corresponds to its state. // @@ -49,7 +51,7 @@ namespace jit { // to find the structure of the frame, and then use the stack and spilled // registers to perform frame conversion. // (5) Bailout() returns, and the JIT must immediately return to the -// interpreter (all frames are converted at once). +// baseline JIT code (all frames are converted at once). // // (2) and (3) are implemented by a trampoline held in the compartment. // Naively, we could implement (1) like: diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp index 95c932e83617..71d572db5c00 100644 --- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -625,7 +625,7 @@ GeneratePrototypeGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder, // [[GetOwnProperty]] has no definition of the target property. // // - // Shape Teleporting Optimization + // [SMDOC] Shape Teleporting Optimization // ------------------------------ // // Starting with the assumption (and guideline to developers) that mutating diff --git a/js/src/jit/CacheIR.h b/js/src/jit/CacheIR.h index 1dd7b5972891..2546f591d52d 100644 --- a/js/src/jit/CacheIR.h +++ b/js/src/jit/CacheIR.h @@ -22,6 +22,8 @@ namespace jit { enum class BaselineCacheIRStubKind; +// [SMDOC] CacheIR +// // CacheIR is an (extremely simple) linear IR language for inline caches. // From this IR, we can generate machine code for Baseline or Ion IC stubs. // diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 630adc2c591a..b9151713a660 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -4418,6 +4418,12 @@ CodeGenerator::visitCallDOMNative(LCallDOMNative* call) masm.Push(argObj); masm.moveStackPtrTo(argObj); + if (call->mir()->maybeCrossRealm()) { + // We use argJSContext as scratch register here. + masm.movePtr(ImmGCPtr(target->rawJSFunction()), argJSContext); + masm.switchToObjectRealm(argJSContext, argJSContext); + } + // Construct native exit frame. uint32_t safepointOffset = masm.buildFakeExitFrame(argJSContext); masm.loadJSContext(argJSContext); @@ -4447,6 +4453,14 @@ CodeGenerator::visitCallDOMNative(LCallDOMNative* call) JSReturnOperand); } + // Switch back to the current realm if needed. Note: if the DOM method threw + // an exception, the exception handler will do this. + if (call->mir()->maybeCrossRealm()) { + static_assert(!JSReturnOperand.aliases(ReturnReg), + "Clobbering ReturnReg should not affect the return value"); + masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); + } + // Until C++ code is instrumented against Spectre, prevent speculative // execution from returning any private data. if (JitOptions.spectreJitToCxxCalls && call->mir()->hasLiveDefUses()) @@ -12255,6 +12269,12 @@ CodeGenerator::visitGetDOMProperty(LGetDOMProperty* ins) // Rooting will happen at GC time. masm.moveStackPtrTo(ObjectReg); + Realm* getterRealm = ins->mir()->getterRealm(); + if (gen->realm->realmPtr() != getterRealm) { + // We use JSContextReg as scratch register here. + masm.switchToRealm(getterRealm, JSContextReg); + } + uint32_t safepointOffset = masm.buildFakeExitFrame(JSContextReg); masm.loadJSContext(JSContextReg); masm.enterFakeExitFrame(JSContextReg, JSContextReg, ExitFrameType::IonDOMGetter); @@ -12280,6 +12300,14 @@ CodeGenerator::visitGetDOMProperty(LGetDOMProperty* ins) JSReturnOperand); } + // Switch back to the current realm if needed. Note: if the getter threw an + // exception, the exception handler will do this. + if (gen->realm->realmPtr() != getterRealm) { + static_assert(!JSReturnOperand.aliases(ReturnReg), + "Clobbering ReturnReg should not affect the return value"); + masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); + } + // Until C++ code is instrumented against Spectre, prevent speculative // execution from returning any private data. if (JitOptions.spectreJitToCxxCalls && ins->mir()->hasLiveDefUses()) @@ -12360,6 +12388,12 @@ CodeGenerator::visitSetDOMProperty(LSetDOMProperty* ins) // Rooting will happen at GC time. masm.moveStackPtrTo(ObjectReg); + Realm* setterRealm = ins->mir()->setterRealm(); + if (gen->realm->realmPtr() != setterRealm) { + // We use JSContextReg as scratch register here. + masm.switchToRealm(setterRealm, JSContextReg); + } + uint32_t safepointOffset = masm.buildFakeExitFrame(JSContextReg); masm.loadJSContext(JSContextReg); masm.enterFakeExitFrame(JSContextReg, JSContextReg, ExitFrameType::IonDOMSetter); @@ -12377,6 +12411,11 @@ CodeGenerator::visitSetDOMProperty(LSetDOMProperty* ins) masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); + // Switch back to the current realm if needed. Note: if the setter threw an + // exception, the exception handler will do this. + if (gen->realm->realmPtr() != setterRealm) + masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); + masm.adjustStack(IonDOMExitFrameLayout::Size()); MOZ_ASSERT(masm.framePushed() == initialStack); diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 2b9ace14f602..c9955e49aa8c 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -11004,7 +11004,8 @@ IonBuilder::getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName // needed. get = MGetDOMMember::New(alloc(), jitinfo, obj, guard, globalGuard); } else { - get = MGetDOMProperty::New(alloc(), jitinfo, objKind, obj, guard, globalGuard); + get = MGetDOMProperty::New(alloc(), jitinfo, objKind, commonGetter->realm(), obj, + guard, globalGuard); } if (!get) return abort(AbortReason::Alloc); @@ -11693,7 +11694,7 @@ IonBuilder::setPropTryCommonDOMSetter(bool* emitted, MDefinition* obj, // Emit SetDOMProperty. MOZ_ASSERT(setter->jitInfo()->type() == JSJitInfo::Setter); MSetDOMProperty* set = MSetDOMProperty::New(alloc(), setter->jitInfo()->setter, objKind, - obj, value); + setter->realm(), obj, value); current->add(set); current->push(value); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 8fe508ceab1c..d5a3f4e17454 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -12385,12 +12385,14 @@ class MSetDOMProperty public MixPolicy, BoxPolicy<1> >::Data { const JSJitSetterOp func_; + Realm* setterRealm_; DOMObjectKind objectKind_; - MSetDOMProperty(const JSJitSetterOp func, DOMObjectKind objectKind, MDefinition* obj, - MDefinition* val) + MSetDOMProperty(const JSJitSetterOp func, DOMObjectKind objectKind, Realm* setterRealm, + MDefinition* obj, MDefinition* val) : MBinaryInstruction(classOpcode, obj, val), func_(func), + setterRealm_(setterRealm), objectKind_(objectKind) { } @@ -12402,6 +12404,9 @@ class MSetDOMProperty JSJitSetterOp fun() const { return func_; } + Realm* setterRealm() const { + return setterRealm_; + } DOMObjectKind objectKind() const { return objectKind_; } @@ -12517,11 +12522,14 @@ class MGetDOMPropertyBase class MGetDOMProperty : public MGetDOMPropertyBase { + Realm* getterRealm_; DOMObjectKind objectKind_; protected: - MGetDOMProperty(const JSJitInfo* jitinfo, DOMObjectKind objectKind) + MGetDOMProperty(const JSJitInfo* jitinfo, DOMObjectKind objectKind, + Realm* getterRealm) : MGetDOMPropertyBase(classOpcode, jitinfo), + getterRealm_(getterRealm), objectKind_(objectKind) {} @@ -12529,14 +12537,18 @@ class MGetDOMProperty INSTRUCTION_HEADER(GetDOMProperty) static MGetDOMProperty* New(TempAllocator& alloc, const JSJitInfo* info, DOMObjectKind objectKind, - MDefinition* obj, MDefinition* guard, MDefinition* globalGuard) + Realm* getterRealm, MDefinition* obj, MDefinition* guard, + MDefinition* globalGuard) { - auto* res = new(alloc) MGetDOMProperty(info, objectKind); + auto* res = new(alloc) MGetDOMProperty(info, objectKind, getterRealm); if (!res || !res->init(alloc, obj, guard, globalGuard)) return nullptr; return res; } + Realm* getterRealm() const { + return getterRealm_; + } DOMObjectKind objectKind() const { return objectKind_; } @@ -12545,6 +12557,9 @@ class MGetDOMProperty if (!ins->isGetDOMProperty()) return false; + if (ins->toGetDOMProperty()->getterRealm() != getterRealm()) + return false; + return baseCongruentTo(ins->toGetDOMProperty()); } diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index 107c11fca5a1..bd5c3f2fe0bc 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -41,6 +41,8 @@ #include "vm/TypedArrayObject.h" #include "vm/UnboxedObject.h" +// [SMDOC] MacroAssembler multi-platform overview +// // * How to read/write MacroAssembler method declarations: // // The following macros are made to avoid #ifdef around each method declarations diff --git a/js/src/jit/RangeAnalysis.cpp b/js/src/jit/RangeAnalysis.cpp index 5c8e95aac967..df67307eceaf 100644 --- a/js/src/jit/RangeAnalysis.cpp +++ b/js/src/jit/RangeAnalysis.cpp @@ -37,6 +37,8 @@ using mozilla::Swap; using JS::GenericNaN; using JS::ToInt32; +// [SMDOC] IonMonkey Range Analysis +// // This algorithm is based on the paper "Eliminating Range Checks Using // Static Single Assignment Form" by Gough and Klaren. // diff --git a/js/src/jit/Recover.h b/js/src/jit/Recover.h index c00b49710e51..696472f9ac2d 100644 --- a/js/src/jit/Recover.h +++ b/js/src/jit/Recover.h @@ -17,6 +17,8 @@ namespace js { namespace jit { +// [SMDOC] IonMonkey Recover Instructions +// // This file contains all recover instructions. // // A recover instruction is an equivalent of a MIR instruction which is executed diff --git a/js/src/jit/RegisterSets.h b/js/src/jit/RegisterSets.h index fe14305e9d7a..9473cb5dd873 100644 --- a/js/src/jit/RegisterSets.h +++ b/js/src/jit/RegisterSets.h @@ -466,6 +466,8 @@ class RegisterSet { } }; +// [SMDOC] JIT Register-Set overview +// // There are 2 use cases for register sets: // // 1. To serve as a pool of allocatable register. This is useful for working @@ -504,6 +506,8 @@ class AllocatableSet; template class LiveSet; +// [SMDOC] JIT Register-Set (Allocatable) +// // Base accessors classes have the minimal set of raw methods to manipulate the register set // given as parameter in a consistent manner. These methods are: // @@ -620,6 +624,8 @@ class AllocatableSetAccessors }; +// [SMDOC] JIT Register-Set (Live) +// // The LiveSet accessors are used to collect a list of allocated // registers. Taking or adding a register should *not* consider the aliases, as // we care about interpreting the registers with the correct type. For example, diff --git a/js/src/jit/SharedIC.h b/js/src/jit/SharedIC.h index 20a421332e8b..b077309ca5dc 100644 --- a/js/src/jit/SharedIC.h +++ b/js/src/jit/SharedIC.h @@ -22,6 +22,7 @@ namespace js { namespace jit { +// [SMDOC] JIT Inline Caches (ICs) // // Baseline Inline Caches are polymorphic caches that aggressively // share their stub code. diff --git a/js/src/jit/Snapshots.cpp b/js/src/jit/Snapshots.cpp index a062e50f1729..47dd04e3fea5 100644 --- a/js/src/jit/Snapshots.cpp +++ b/js/src/jit/Snapshots.cpp @@ -19,6 +19,8 @@ using namespace js; using namespace js::jit; +// [SMDOC] IonMonkey Snapshot encoding +// // Encodings: // [ptr] A fixed-size pointer. // [vwu] A variable-width unsigned integer. diff --git a/js/src/jit/ValueNumbering.cpp b/js/src/jit/ValueNumbering.cpp index e480ad2d3175..9a2f96541c08 100644 --- a/js/src/jit/ValueNumbering.cpp +++ b/js/src/jit/ValueNumbering.cpp @@ -15,6 +15,8 @@ using namespace js; using namespace js::jit; /* + * [SMDOC] IonMonkey Value Numbering + * * Some notes on the main algorithm here: * - The SSA identifier id() is the value number. We do replaceAllUsesWith as * we go, so there's always at most one visible value with a given number. diff --git a/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h b/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h index b0256526f7f2..cfeb3e1b774f 100644 --- a/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h +++ b/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h @@ -14,6 +14,8 @@ #include "jit/JitSpewer.h" #include "jit/shared/IonAssemblerBuffer.h" +// [SMDOC] JIT AssemblerBuffer constant pooling (ARM/ARM64/MIPS) +// // This code extends the AssemblerBuffer to support the pooling of values loaded // using program-counter relative addressing modes. This is necessary with the // ARM instruction set because it has a fixed instruction size that can not diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 66a76ec626e0..89994a4b6bba 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -6137,36 +6137,21 @@ JS_EncodeStringToBuffer(JSContext* cx, JSString* str, char* buffer, size_t lengt AssertHeapIsIdle(); CHECK_REQUEST(cx); - /* - * FIXME bug 612141 - fix DeflateStringToBuffer interface so the result - * would allow to distinguish between insufficient buffer and encoding - * error. - */ - size_t writtenLength = length; JSLinearString* linear = str->ensureLinear(cx); if (!linear) return size_t(-1); - bool res; + JS::AutoCheckCannotGC nogc; + size_t writeLength = Min(linear->length(), length); if (linear->hasLatin1Chars()) { - JS::AutoCheckCannotGC nogc; - res = DeflateStringToBuffer(nullptr, linear->latin1Chars(nogc), linear->length(), buffer, - &writtenLength); + mozilla::PodCopy(reinterpret_cast(buffer), linear->latin1Chars(nogc), + writeLength); } else { - JS::AutoCheckCannotGC nogc; - res = DeflateStringToBuffer(nullptr, linear->twoByteChars(nogc), linear->length(), buffer, - &writtenLength); + const char16_t* src = linear->twoByteChars(nogc); + for (size_t i = 0; i < writeLength; i++) + buffer[i] = char(src[i]); } - if (res) { - MOZ_ASSERT(writtenLength <= length); - return writtenLength; - } - MOZ_ASSERT(writtenLength <= length); - size_t necessaryLength = str->length(); - if (necessaryLength == size_t(-1)) - return size_t(-1); - MOZ_ASSERT(writtenLength == length); // C strings are NOT encoded. - return necessaryLength; + return linear->length(); } JS_PUBLIC_API(JS::Symbol*) diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 8e01dba29254..718c6a8a9627 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -7839,11 +7839,43 @@ dom_set_x(JSContext* cx, HandleObject obj, void* self, JSJitSetterCallArgs args) return true; } +static bool +dom_get_global(JSContext* cx, HandleObject obj, void* self, JSJitGetterCallArgs args) +{ + MOZ_ASSERT(JS_GetClass(obj) == GetDomClass()); + MOZ_ASSERT(self == (void*)0x1234); + + // Return the current global (instead of obj->global()) to test cx->realm + // switching in the JIT. + args.rval().setObject(*ToWindowProxyIfWindow(cx->global())); + + return true; +} + +static bool +dom_set_global(JSContext* cx, HandleObject obj, void* self, JSJitSetterCallArgs args) +{ + MOZ_ASSERT(JS_GetClass(obj) == GetDomClass()); + MOZ_ASSERT(self == (void*)0x1234); + + // Throw an exception if our argument is not the current global. This lets + // us test cx->realm switching. + if (!args[0].isObject() || + ToWindowIfWindowProxy(&args[0].toObject()) != cx->global()) + { + JS_ReportErrorASCII(cx, "Setter not called with matching global argument"); + return false; + } + + return true; +} + static bool dom_doFoo(JSContext* cx, HandleObject obj, void* self, const JSJitMethodCallArgs& args) { MOZ_ASSERT(JS_GetClass(obj) == GetDomClass()); MOZ_ASSERT(self == (void*)0x1234); + MOZ_ASSERT(cx->realm() == args.callee().as().realm()); /* Just return args.length(). */ args.rval().setInt32(args.length()); @@ -7854,8 +7886,8 @@ static const JSJitInfo dom_x_getterinfo = { { (JSJitGetterOp)dom_get_x }, { 0 }, /* protoID */ { 0 }, /* depth */ - JSJitInfo::AliasNone, /* aliasSet */ JSJitInfo::Getter, + JSJitInfo::AliasNone, /* aliasSet */ JSVAL_TYPE_UNKNOWN, /* returnType */ true, /* isInfallible. False in setters. */ true, /* isMovable */ @@ -7882,6 +7914,41 @@ static const JSJitInfo dom_x_setterinfo = { 0 /* slotIndex */ }; +// Note: this getter uses AliasEverything and is marked as fallible and +// non-movable (1) to prevent Ion from getting too clever optimizing it and +// (2) it's nice to have a few different kinds of getters in the shell. +static const JSJitInfo dom_global_getterinfo = { + { (JSJitGetterOp)dom_get_global }, + { 0 }, /* protoID */ + { 0 }, /* depth */ + JSJitInfo::Getter, + JSJitInfo::AliasEverything, /* aliasSet */ + JSVAL_TYPE_OBJECT, /* returnType */ + false, /* isInfallible. False in setters. */ + false, /* isMovable */ + false, /* isEliminatable */ + false, /* isAlwaysInSlot */ + false, /* isLazilyCachedInSlot */ + false, /* isTypedMethod */ + 0 /* slotIndex */ +}; + +static const JSJitInfo dom_global_setterinfo = { + { (JSJitGetterOp)dom_set_global }, + { 0 }, /* protoID */ + { 0 }, /* depth */ + JSJitInfo::Setter, + JSJitInfo::AliasEverything, /* aliasSet */ + JSVAL_TYPE_UNKNOWN, /* returnType */ + false, /* isInfallible. False in setters. */ + false, /* isMovable. */ + false, /* isEliminatable. */ + false, /* isAlwaysInSlot */ + false, /* isLazilyCachedInSlot */ + false, /* isTypedMethod */ + 0 /* slotIndex */ +}; + static const JSJitInfo doFoo_methodinfo = { { (JSJitGetterOp)dom_doFoo }, { 0 }, /* protoID */ @@ -7906,6 +7973,13 @@ static const JSPropertySpec dom_props[] = { { { dom_genericSetter, &dom_x_setterinfo } } } }, }, + {"global", + JSPROP_ENUMERATE, + { { + { { dom_genericGetter, &dom_global_getterinfo } }, + { { dom_genericSetter, &dom_global_setterinfo } } + } }, + }, JS_PS_END }; diff --git a/js/src/threading/ExclusiveData.h b/js/src/threading/ExclusiveData.h index d64e854d4a80..4733f8431dbf 100644 --- a/js/src/threading/ExclusiveData.h +++ b/js/src/threading/ExclusiveData.h @@ -17,6 +17,8 @@ namespace js { /** + * [SMDOC] ExclusiveData API + * * A mutual exclusion lock class. * * `ExclusiveData` provides an RAII guard to automatically lock and unlock when diff --git a/js/src/util/Text.cpp b/js/src/util/Text.cpp index fee1f7bbd819..6d3599469a07 100644 --- a/js/src/util/Text.cpp +++ b/js/src/util/Text.cpp @@ -119,36 +119,6 @@ js::InflateString(JSContext* cx, const char* bytes, size_t length) return chars; } -template -bool -js::DeflateStringToBuffer(JSContext* maybecx, const CharT* src, size_t srclen, - char* dst, size_t* dstlenp) -{ - size_t dstlen = *dstlenp; - if (srclen > dstlen) { - for (size_t i = 0; i < dstlen; i++) - dst[i] = char(src[i]); - if (maybecx) { - AutoSuppressGC suppress(maybecx); - JS_ReportErrorNumberASCII(maybecx, GetErrorMessage, nullptr, - JSMSG_BUFFER_TOO_SMALL); - } - return false; - } - for (size_t i = 0; i < srclen; i++) - dst[i] = char(src[i]); - *dstlenp = srclen; - return true; -} - -template bool -js::DeflateStringToBuffer(JSContext* maybecx, const Latin1Char* src, size_t srclen, - char* dst, size_t* dstlenp); - -template bool -js::DeflateStringToBuffer(JSContext* maybecx, const char16_t* src, size_t srclen, - char* dst, size_t* dstlenp); - /* * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at * least 4 bytes long. Return the number of UTF-8 bytes of data written. diff --git a/js/src/util/Text.h b/js/src/util/Text.h index f9a5deeb4a94..da48d0bbcd51 100644 --- a/js/src/util/Text.h +++ b/js/src/util/Text.h @@ -152,17 +152,6 @@ CopyAndInflateChars(char16_t* dst, const JS::Latin1Char* src, size_t srclen) dst[i] = src[i]; } -/* - * Deflate JS chars to bytes into a buffer. 'bytes' must be large enough for - * 'length chars. The buffer is NOT null-terminated. The destination length - * must to be initialized with the buffer size and will contain on return the - * number of copied bytes. - */ -template -extern bool -DeflateStringToBuffer(JSContext* maybecx, const CharT* chars, - size_t charsLength, char* bytes, size_t* length); - /* * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at * least 4 bytes long. Return the number of UTF-8 bytes of data written. diff --git a/js/src/vm/ArgumentsObject.h b/js/src/vm/ArgumentsObject.h index fb8389478cf5..28e319c14916 100644 --- a/js/src/vm/ArgumentsObject.h +++ b/js/src/vm/ArgumentsObject.h @@ -98,6 +98,8 @@ struct ArgumentsData static const unsigned ARGS_LENGTH_MAX = 500 * 1000; /* + * [SMDOC] ArgumentsObject + * * ArgumentsObject instances represent |arguments| objects created to store * function arguments when a function is called. It's expensive to create such * objects if they're never used, so they're only created when they are diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp index 96aeb9100701..1b3ec7bc5e0d 100644 --- a/js/src/vm/ArrayBufferObject.cpp +++ b/js/src/vm/ArrayBufferObject.cpp @@ -592,6 +592,8 @@ ArrayBufferObject::changeContents(JSContext* cx, BufferContents newContents, } /* + * [SMDOC] WASM Linear Memory structure + * * Wasm Raw Buf Linear Memory Structure * * The linear heap in Wasm is an mmaped array buffer. Several diff --git a/js/src/vm/BytecodeUtil.h b/js/src/vm/BytecodeUtil.h index 8707fe9c0ea7..ec4fb4da9a99 100644 --- a/js/src/vm/BytecodeUtil.h +++ b/js/src/vm/BytecodeUtil.h @@ -35,7 +35,7 @@ FOR_EACH_OPCODE(ENUMERATE_OPCODE) } JSOp; /* - * JS bytecode formats. + * [SMDOC] Bytecode Format flags (JOF_*) */ enum { JOF_BYTE = 0, /* single bytecode, no immediates */ diff --git a/js/src/vm/EnvironmentObject.h b/js/src/vm/EnvironmentObject.h index 8f9cb5a42f2b..415a0ced0568 100644 --- a/js/src/vm/EnvironmentObject.h +++ b/js/src/vm/EnvironmentObject.h @@ -45,6 +45,8 @@ EnvironmentCoordinateFunctionScript(JSScript* script, jsbytecode* pc); /*** Environment objects *****************************************************/ /* + * [SMDOC] Environment Objects + * * About environments * ------------------ * diff --git a/js/src/vm/JSObject.h b/js/src/vm/JSObject.h index 313c2d898407..16728c289de6 100644 --- a/js/src/vm/JSObject.h +++ b/js/src/vm/JSObject.h @@ -49,6 +49,8 @@ bool SetImmutablePrototype(JSContext* cx, JS::HandleObject obj, bool* succeeded) } /* namespace js */ /* + * [SMDOC] JSObject layout + * * A JavaScript object. * * This is the base class for all objects exposed to JS script (as well as some diff --git a/js/src/vm/JSScript.cpp b/js/src/vm/JSScript.cpp index 4bd158f80028..0735975dca55 100644 --- a/js/src/vm/JSScript.cpp +++ b/js/src/vm/JSScript.cpp @@ -2332,6 +2332,8 @@ ScriptSource::setSourceMapURL(JSContext* cx, const char16_t* sourceMapURL) } /* + * [SMDOC] JSScript data layout (shared) + * * Shared script data management. * * SharedScriptData::data contains data that can be shared within a @@ -2505,6 +2507,8 @@ js::FreeScriptData(JSRuntime* rt) } /* + * [SMDOC] JSScript data layout (unshared) + * * JSScript::data and SharedScriptData::data have complex, * manually-controlled, memory layouts. * diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp index 54d85b43ba83..2a56eebb6413 100644 --- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -1194,6 +1194,17 @@ CallAddPropertyHookDense(JSContext* cx, HandleNativeObject obj, uint32_t index, return true; } +/** + * Determines whether a write to the given element on |arr| should fail + * because |arr| has a non-writable length, and writing that element would + * increase the length of the array. + */ +static bool +WouldDefinePastNonwritableLength(ArrayObject* arr, uint32_t index) +{ + return !arr->lengthIsWritable() && index >= arr->length(); +} + static MOZ_ALWAYS_INLINE void UpdateShapeTypeAndValue(JSContext* cx, NativeObject* obj, Shape* shape, jsid id, const Value& value) @@ -1621,7 +1632,7 @@ js::NativeDefineProperty(JSContext* cx, HandleNativeObject obj, HandleId id, // 9.4.2.1 step 3. Don't extend a fixed-length array. uint32_t index; if (IdIsIndex(id, &index)) { - if (WouldDefinePastNonwritableLength(obj, index)) + if (WouldDefinePastNonwritableLength(arr, index)) return result.fail(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH); } } else if (obj->is()) { @@ -1923,7 +1934,7 @@ DefineNonexistentProperty(JSContext* cx, HandleNativeObject obj, HandleId id, // 9.4.2.1 step 3. Don't extend a fixed-length array. uint32_t index; if (IdIsIndex(id, &index)) { - if (WouldDefinePastNonwritableLength(obj, index)) + if (WouldDefinePastNonwritableLength(&obj->as(), index)) return result.fail(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH); } } else if (obj->is()) { @@ -2647,11 +2658,14 @@ SetDenseOrTypedArrayElement(JSContext* cx, HandleNativeObject obj, uint32_t inde return result.succeed(); } - return result.failSoft(JSMSG_BAD_INDEX); + // A previously existing typed array element can only be out-of-bounds + // if the above ToNumber call detached the typed array's buffer. + MOZ_ASSERT(obj->as().hasDetachedBuffer()); + + return result.failSoft(JSMSG_TYPED_ARRAY_DETACHED); } - if (WouldDefinePastNonwritableLength(obj, index)) - return result.fail(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH); + MOZ_ASSERT(obj->containsDenseElement(index)); if (!obj->maybeCopyElementsForWrite(cx)) return false; @@ -2669,9 +2683,8 @@ SetDenseOrTypedArrayElement(JSContext* cx, HandleNativeObject obj, uint32_t inde * dense or typed array element (i.e. not actually a pointer to a Shape). */ static bool -SetExistingProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue v, - HandleValue receiver, HandleNativeObject pobj, Handle prop, - ObjectOpResult& result) +SetExistingProperty(JSContext* cx, HandleId id, HandleValue v, HandleValue receiver, + HandleNativeObject pobj, Handle prop, ObjectOpResult& result) { // Step 5 for dense elements. if (prop.isDenseOrTypedArrayElement()) { @@ -2744,7 +2757,7 @@ js::NativeSetProperty(JSContext* cx, HandleNativeObject obj, HandleId id, Handle if (prop) { // Steps 5-6. - return SetExistingProperty(cx, obj, id, v, receiver, pobj, prop, result); + return SetExistingProperty(cx, id, v, receiver, pobj, prop, result); } // Steps 4.a-b. The check for 'done' on this next line is tricky. diff --git a/js/src/vm/NativeObject.h b/js/src/vm/NativeObject.h index a010c91e3149..9b5bcf585dfa 100644 --- a/js/src/vm/NativeObject.h +++ b/js/src/vm/NativeObject.h @@ -92,6 +92,8 @@ ArraySetLength(JSContext* cx, Handle obj, HandleId id, unsigned attrs, HandleValue value, ObjectOpResult& result); /* + * [SMDOC] NativeObject Elements layout + * * Elements header used for native objects. The elements component of such objects * offers an efficient representation for all or some of the indexed properties * of the object, using a flat array of Values rather than a shape hierarchy @@ -159,6 +161,9 @@ ArraySetLength(JSContext* cx, Handle obj, HandleId id, * of an object does not necessarily visit indexes in the order they were * created. * + * + * [SMDOC] NativeObject shifted elements optimization + * * Shifted elements * ---------------- * It's pretty common to use an array as a queue, like this: @@ -432,6 +437,8 @@ enum class ShouldUpdateTypes { }; /* + * [SMDOC] NativeObject layout + * * NativeObject specifies the internal implementation of a native object. * * Native objects use ShapedObject::shape to record property information. Two diff --git a/js/src/vm/ObjectGroup.h b/js/src/vm/ObjectGroup.h index d1e161cae311..a651b32b6a3f 100644 --- a/js/src/vm/ObjectGroup.h +++ b/js/src/vm/ObjectGroup.h @@ -65,7 +65,7 @@ enum NewObjectKind { }; /* - * Lazy object groups overview. + * [SMDOC] Type-Inference lazy ObjectGroup * * Object groups which represent at most one JS object are constructed lazily. * These include groups for native functions, standard classes, scripted @@ -109,6 +109,8 @@ class ObjectGroup : public gc::TenuredCell void* addendum_ = nullptr; /* + * [SMDOC] Type-Inference object properties + * * Properties of this object. * * The type sets in the properties of a group describe the possible values diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index 28d30bef3fcb..111dbcdb1929 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -13,6 +13,8 @@ #include /* + * [SMDOC] Bytecode Definitions + * * JavaScript operation bytecodes. Add a new bytecode by claiming one of the * JSOP_UNUSED* here or by extracting the first unused opcode from * FOR_EACH_TRAILING_UNUSED_OPCODE and updating js::detail::LastDefinedOpcode diff --git a/js/src/vm/Printer.cpp b/js/src/vm/Printer.cpp index fa400c37c156..6d696778fe1c 100644 --- a/js/src/vm/Printer.cpp +++ b/js/src/vm/Printer.cpp @@ -223,9 +223,8 @@ Sprinter::putString(JSString* s) InvariantChecker ic(this); size_t length = s->length(); - size_t size = length; - char* buffer = reserve(size); + char* buffer = reserve(length); if (!buffer) return false; @@ -234,12 +233,15 @@ Sprinter::putString(JSString* s) return false; JS::AutoCheckCannotGC nogc; - if (linear->hasLatin1Chars()) + if (linear->hasLatin1Chars()) { PodCopy(reinterpret_cast(buffer), linear->latin1Chars(nogc), length); - else - DeflateStringToBuffer(nullptr, linear->twoByteChars(nogc), length, buffer, &size); + } else { + const char16_t* src = linear->twoByteChars(nogc); + for (size_t i = 0; i < length; i++) + buffer[i] = char(src[i]); + } - buffer[size] = 0; + buffer[length] = 0; return true; } diff --git a/js/src/vm/Realm.h b/js/src/vm/Realm.h index 9431e4f83cd3..810aedeef08d 100644 --- a/js/src/vm/Realm.h +++ b/js/src/vm/Realm.h @@ -119,6 +119,8 @@ class NewProxyCache } }; +// [SMDOC] Object MetadataBuilder API +// // We must ensure that all newly allocated JSObjects get their metadata // set. However, metadata builders may require the new object be in a sane // state (eg, have its reserved slots initialized so they can get the diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index bd1f6d055d7e..ff2aba956240 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -107,7 +107,7 @@ class Simulator; #endif } // namespace jit -// JS Engine Threading +// [SMDOC] JS Engine Threading // // Threads interacting with a runtime are divided into two categories: // diff --git a/js/src/vm/Shape.h b/js/src/vm/Shape.h index 309d3c52b05c..7d5419b8be63 100644 --- a/js/src/vm/Shape.h +++ b/js/src/vm/Shape.h @@ -34,6 +34,8 @@ #include "vm/SymbolType.h" /* + * [SMDOC] Shapes + * * In isolation, a Shape represents a property that exists in one or more * objects; it has an id, flags, etc. (But it doesn't represent the property's * value.) However, Shapes are always stored in linked linear sequence of diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index ed6ff34f7528..4c071eca603e 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -69,7 +69,7 @@ class DebugFrame; class Instance; } -// VM stack layout +// [SMDOC] VM stack layout // // A JSRuntime's stack consists of a linked list of activations. Every activation // contains a number of scripted frames that are either running in the interpreter diff --git a/js/src/vm/StringType.h b/js/src/vm/StringType.h index 8638fe7ff8b3..7d5e87e7c91c 100644 --- a/js/src/vm/StringType.h +++ b/js/src/vm/StringType.h @@ -44,7 +44,7 @@ static const size_t UINT32_CHAR_BUFFER_LENGTH = sizeof("4294967295") - 1; } /* namespace js */ /* - * JavaScript strings + * [SMDOC] JavaScript Strings * * Conceptually, a JS string is just an array of chars and a length. This array * of chars may or may not be null-terminated and, if it is, the null character diff --git a/js/src/vm/SymbolType.h b/js/src/vm/SymbolType.h index 49413805452e..88405162f133 100644 --- a/js/src/vm/SymbolType.h +++ b/js/src/vm/SymbolType.h @@ -122,6 +122,8 @@ struct HashSymbolsByDescription }; /* + * [SMDOC] Symbol.for() registry (ES6 GlobalSymbolRegistry) + * * The runtime-wide symbol registry, used to implement Symbol.for(). * * ES6 draft rev 25 (2014 May 22) calls this the GlobalSymbolRegistry List. In diff --git a/js/src/vm/TypeInference.h b/js/src/vm/TypeInference.h index 5280308da4df..6a06c6437f23 100644 --- a/js/src/vm/TypeInference.h +++ b/js/src/vm/TypeInference.h @@ -213,6 +213,8 @@ class HeapTypeSet; class TemporaryTypeSet; /* + * [SMDOC] Type-Inference TypeSet + * * Information about the set of types associated with an lvalue. There are * three kinds of type sets: * diff --git a/js/src/wasm/WasmBaselineCompile.cpp b/js/src/wasm/WasmBaselineCompile.cpp index 69df2144f501..138a4666df18 100644 --- a/js/src/wasm/WasmBaselineCompile.cpp +++ b/js/src/wasm/WasmBaselineCompile.cpp @@ -16,7 +16,8 @@ * limitations under the License. */ -/* WebAssembly baseline compiler ("RabaldrMonkey") +/* + * [SMDOC] WebAssembly baseline compiler (RabaldrMonkey) * * General assumptions for 32-bit vs 64-bit code: * diff --git a/js/src/wasm/WasmTextToBinary.cpp b/js/src/wasm/WasmTextToBinary.cpp index df066677be6d..d6bce593b843 100644 --- a/js/src/wasm/WasmTextToBinary.cpp +++ b/js/src/wasm/WasmTextToBinary.cpp @@ -1656,15 +1656,19 @@ WasmTokenStream::next() break; case 'm': -#ifdef ENABLE_WASM_BULKMEM_OPS if (consume(u"memory.")) { +#ifdef ENABLE_WASM_BULKMEM_OPS if (consume(u"copy")) return WasmToken(WasmToken::MemCopy, begin, cur_); if (consume(u"fill")) return WasmToken(WasmToken::MemFill, begin, cur_); +#endif + if (consume(u"grow")) + return WasmToken(WasmToken::GrowMemory, begin, cur_); + if (consume(u"size")) + return WasmToken(WasmToken::CurrentMemory, begin, cur_); break; } -#endif if (consume(u"module")) return WasmToken(WasmToken::Module, begin, cur_); if (consume(u"memory")) diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp index 4f80119de79b..4c523c79d0ec 100644 --- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp @@ -176,6 +176,8 @@ #include "nsLayoutStylesheetCache.h" #include "mozilla/layers/InputAPZContext.h" #include "mozilla/layers/FocusTarget.h" +#include "mozilla/layers/WebRenderLayerManager.h" +#include "mozilla/layers/WebRenderUserData.h" #include "mozilla/ServoBindings.h" #include "mozilla/ServoStyleSet.h" #include "mozilla/StyleSheet.h" @@ -6319,7 +6321,15 @@ PresShell::Paint(nsView* aViewToPaint, } if (layerManager->GetBackendType() == layers::LayersBackend::LAYERS_WR) { - // TODO: bug 1405465 - create a WR display list which simulates the color layer below. + nsPresContext* pc = GetPresContext(); + LayoutDeviceRect bounds = + LayoutDeviceRect::FromAppUnits(pc->GetVisibleArea(), pc->AppUnitsPerDevPixel()); + bgcolor = NS_ComposeColors(bgcolor, mCanvasBackgroundColor); + WebRenderBackgroundData data(wr::ToLayoutRect(bounds), wr::ToColorF(ToDeviceColor(bgcolor))); + nsTArray wrFilters; + + MaybeSetupTransactionIdAllocator(layerManager, presContext); + layerManager->AsWebRenderLayerManager()->EndTransactionWithoutLayer(nullptr, nullptr, wrFilters, &data); return; } diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 2202c6ae7177..672097f8ae8b 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -1916,6 +1916,11 @@ nsCSSFrameConstructor::CreateGeneratedContentItem(nsFrameConstructorState& aStat CreateGeneratedContent(aState, aParentContent, pseudoComputedStyle, contentIndex); if (content) { + // We don't strictly have to set NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE + // here; it would get set under AppendChildTo. But AppendChildTo might + // think that we're going from not being anonymous to being anonymous and + // do some extra work; setting the flag here avoids that. + content->SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE); container->AppendChildTo(content, false); if (content->IsElement()) { // If we created any children elements, Servo needs to traverse them, but diff --git a/layout/reftests/border-radius/reftest.list b/layout/reftests/border-radius/reftest.list index 9ff5d49ce5d6..450e8feac85b 100644 --- a/layout/reftests/border-radius/reftest.list +++ b/layout/reftests/border-radius/reftest.list @@ -58,7 +58,7 @@ fuzzy-if(cocoaWidget,1,4) fuzzy-if(d2d,59,342) fuzzy-if(d3d11&&advancedLayers&&! == intersecting-clipping-1-image.html intersecting-clipping-1-refi.html == intersecting-clipping-1-overflow-hidden.html intersecting-clipping-1-ref.html fuzzy-if(Android,5,105) fuzzy-if(d2d,1,20) fuzzy-if(skiaContent,1,300) == intersecting-clipping-1-refi.html intersecting-clipping-1-ref.html -fuzzy-if(true,1,33) fuzzy-if(d2d,59,350) fuzzy-if(cocoaWidget,1,332) fuzzy-if(Android,124,440) fuzzy-if(skiaContent,1,135) fuzzy-if(d3d11&&advancedLayers,81,353) == intersecting-clipping-1-refc.html intersecting-clipping-1-ref.html # bug 732535 +fuzzy-if(true,1,33) fuzzy-if(d2d,59,350) fuzzy-if(cocoaWidget,1,332) fuzzy-if(Android,124,440) fuzzy-if(skiaContent,1,135) fuzzy-if(d3d11&&advancedLayers,81,353) skip-if(winWidget) == intersecting-clipping-1-refc.html intersecting-clipping-1-ref.html # bug 732535 # Disable on Windows bug 1451808 # Inheritance == inherit-1.html inherit-1-ref.html # border-radius shouldn't inherit diff --git a/media/libcubeb/cubeb-pulse-rs/README_MOZILLA b/media/libcubeb/cubeb-pulse-rs/README_MOZILLA index 8a9dd50be1ab..259be71b228d 100644 --- a/media/libcubeb/cubeb-pulse-rs/README_MOZILLA +++ b/media/libcubeb/cubeb-pulse-rs/README_MOZILLA @@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system. The cubeb-pulse-rs git repository is: https://github.com/djg/cubeb-pulse-rs.git -The git commit ID used was f90aecf00ed6e5c67f593b3ecf412a8f3ffc0b1f (2018-04-18 08:06:35 +1000) +The git commit ID used was d0bdf51ebd0a653cc4276d2346db852a3060ade0 (2018-06-29 10:09:52 +1000) diff --git a/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/stream.rs b/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/stream.rs index 780c3eb300cb..785ee1be485d 100644 --- a/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/stream.rs +++ b/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/stream.rs @@ -323,8 +323,8 @@ impl Stream { Ok(unsafe { operation::from_raw_ptr(r) }) } - pub fn get_time(&self) -> Result<(u64)> { - let mut usec: u64 = 0; + pub fn get_time(&self) -> Result<(USec)> { + let mut usec: USec = 0; let r = unsafe { ffi::pa_stream_get_time(self.raw_mut(), &mut usec) }; error_result!(usec, r) } diff --git a/media/libcubeb/cubeb-pulse-rs/src/backend/stream.rs b/media/libcubeb/cubeb-pulse-rs/src/backend/stream.rs index 3c6136968dd6..96bee058f0b6 100644 --- a/media/libcubeb/cubeb-pulse-rs/src/backend/stream.rs +++ b/media/libcubeb/cubeb-pulse-rs/src/backend/stream.rs @@ -438,7 +438,7 @@ impl<'ctx> StreamOps for PulseStream<'ctx> { let stm = self.output_stream.as_ref().unwrap(); let r = match stm.get_time() { Ok(r_usec) => { - let bytes = r_usec.to_bytes(&self.output_sample_spec); + let bytes = USecExt::to_bytes(r_usec, &self.output_sample_spec); Ok((bytes / self.output_sample_spec.frame_size()) as u64) } Err(_) => Err(Error::error()), diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 3c43a0ff2ade..ab5ebd537cc2 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -46,10 +46,6 @@ $(ABS_DIST)/fennec/$(OMNIJAR_NAME): FORCE rsync --update $(DIST)/fennec/$(notdir $(OMNIJAR_NAME)) $@ $(RM) $(DIST)/fennec/$(notdir $(OMNIJAR_NAME)) -.PHONY: features -features:: - $(call py_action,generate_builtin_addons,--features=features chrome/chrome/content/built_in_addons.json) - ifndef MOZILLA_OFFICIAL # Targets built very early during a Gradle build. In automation, # these are built before Gradle is invoked, and gradle-targets is not diff --git a/mozglue/build/WindowsDllBlocklistCommon.h b/mozglue/build/WindowsDllBlocklistCommon.h index bc5e96f2b9b5..1b2f871eca7e 100644 --- a/mozglue/build/WindowsDllBlocklistCommon.h +++ b/mozglue/build/WindowsDllBlocklistCommon.h @@ -53,6 +53,15 @@ struct DllBlockInfoT { // Convert the 4 (decimal) components of a DLL version number into a // single unsigned long long, as needed by the blocklist +#if defined(_MSC_VER) && !defined(__clang__) + +// MSVC does not properly handle the constexpr MAKE_VERSION, so we use a macro +// instead (ugh). +#define MAKE_VERSION(a,b,c,d) \ + ((a##ULL << 48) + (b##ULL << 32) + (c##ULL << 16) + d##ULL) + +#else + static inline constexpr uint64_t MAKE_VERSION(uint16_t a, uint16_t b, uint16_t c, uint16_t d) { @@ -62,6 +71,8 @@ MAKE_VERSION(uint16_t a, uint16_t b, uint16_t c, uint16_t d) static_cast(d); } +#endif + #if !defined(DLL_BLOCKLIST_CHAR_TYPE) #error "You must define DLL_BLOCKLIST_CHAR_TYPE" #endif // !defined(DLL_BLOCKLIST_CHAR_TYPE) diff --git a/netwerk/protocol/websocket/WebSocketChannel.cpp b/netwerk/protocol/websocket/WebSocketChannel.cpp index 4bf4982fb888..ca5cc2cfd2b9 100644 --- a/netwerk/protocol/websocket/WebSocketChannel.cpp +++ b/netwerk/protocol/websocket/WebSocketChannel.cpp @@ -59,6 +59,7 @@ #include "mozilla/Telemetry.h" #include "mozilla/TimeStamp.h" #include "nsSocketTransportService2.h" +#include "nsINSSErrorsService.h" #include "plbase64.h" #include "prmem.h" @@ -3817,9 +3818,25 @@ WebSocketChannel::OnStartRequest(nsIRequest *aRequest, rv = mHttpChannel->GetResponseStatus(&status); if (NS_FAILED(rv)) { + nsresult httpStatus; + rv = NS_ERROR_CONNECTION_REFUSED; + + // If we failed to connect due to unsuccessful TLS handshake, we must + // propagate a specific error to mozilla::dom::WebSocketImpl so it can set + // status code to 1015. Otherwise return NS_ERROR_CONNECTION_REFUSED. + if (NS_SUCCEEDED(mHttpChannel->GetStatus(&httpStatus))) { + uint32_t errorClass; + nsCOMPtr errSvc = + do_GetService("@mozilla.org/nss_errors_service;1"); + // If GetErrorClass succeeds httpStatus is TLS related failure. + if (errSvc && NS_SUCCEEDED(errSvc->GetErrorClass(httpStatus, &errorClass))) { + rv = NS_ERROR_NET_INADEQUATE_SECURITY; + } + } + LOG(("WebSocketChannel::OnStartRequest: No HTTP Response\n")); - AbortSession(NS_ERROR_CONNECTION_REFUSED); - return NS_ERROR_CONNECTION_REFUSED; + AbortSession(rv); + return rv; } LOG(("WebSocketChannel::OnStartRequest: HTTP status %d\n", status)); diff --git a/python/mozbuild/mozbuild/action/generate_builtin_addons.py b/python/mozbuild/mozbuild/action/generate_builtin_addons.py deleted file mode 100644 index c65795b7486c..000000000000 --- a/python/mozbuild/mozbuild/action/generate_builtin_addons.py +++ /dev/null @@ -1,52 +0,0 @@ -# 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/. - -from __future__ import absolute_import, print_function, unicode_literals -from mozbuild.util import ensureParentDir - -import argparse -import json -import os.path -import sys - -import buildconfig - - -def main(argv): - parser = argparse.ArgumentParser( - description='Produces a JSON manifest of built-in add-ons') - parser.add_argument('--features', type=str, dest='featuresdir', - action='store', help=('The distribution sub-directory ' - 'containing feature add-ons')) - parser.add_argument('outputfile', help='File to write output to') - args = parser.parse_args(argv) - - bindir = os.path.join(buildconfig.topobjdir, 'dist/bin') - - def find_dictionaries(path): - dicts = {} - for filename in os.listdir(os.path.join(bindir, path)): - base, ext = os.path.splitext(filename) - if ext == '.dic': - dicts[base] = '%s/%s' % (path, filename) - return dicts - - listing = { - "dictionaries": find_dictionaries("dictionaries"), - } - if args.featuresdir: - listing["system"] = sorted(os.listdir(os.path.join(bindir, - args.featuresdir))) - if len(listing["system"]) == 0: - raise IOError("featuresdir is empty, we lost a race") - - outputfilepath = os.path.join(bindir, args.outputfile) - ensureParentDir(outputfilepath) - - with open(outputfilepath, 'w') as fh: - json.dump(listing, fh, sort_keys=True) - - -if __name__ == '__main__': - sys.exit(main(sys.argv[1:])) diff --git a/python/mozbuild/mozbuild/backend/tup.py b/python/mozbuild/mozbuild/backend/tup.py index 94ebf296b74b..af481a33ff4c 100644 --- a/python/mozbuild/mozbuild/backend/tup.py +++ b/python/mozbuild/mozbuild/backend/tup.py @@ -99,6 +99,7 @@ class BackendTupfile(object): # These files are special, ignore anything that generates them or # depends on them. self._skip_files = [ + 'built_in_addons.json', 'signmar', ] diff --git a/taskcluster/docs/docker-images.rst b/taskcluster/docs/docker-images.rst index aa9f38c4a45e..da2cd25a7274 100644 --- a/taskcluster/docs/docker-images.rst +++ b/taskcluster/docs/docker-images.rst @@ -107,11 +107,9 @@ Example: image: taskcluster/decision:0.1.10@sha256:c5451ee6c655b3d97d4baa3b0e29a5115f23e0991d4f7f36d2a8f793076d6854 -Each image has a repo digest, an image hash, and a version. The repo digest is -stored in the ``HASH`` file in the image directory and used to refer to the -image as above. The version is in ``VERSION``. The image hash is used in -`chain-of-trust verification `_ -in `scriptworker `_. +Each image has a repo digest and a version. The repo digest is stored in the +``HASH`` file in the image directory and used to refer to the image as above. +The version is in ``VERSION``. The version file only serves to provide convenient names, such that old versions are easy to discover in the registry (and ensuring old versions aren't @@ -171,12 +169,7 @@ the docker registry and make note of the resulting repo digest. Put this value in the ``HASH`` file, and update any references to the image in the code or task definitions. -The change is now safe to use in Try pushes. However, if the image is used in -building releases then it is *not* safe to land to an integration branch until -the whitelists in `scriptworker's constants.py -`_ -have also been updated. These whitelists use the image hash, not the repo -digest. +The change is now safe to use in Try pushes. Special Dockerfile Syntax ------------------------- diff --git a/testing/mozharness/scripts/desktop_unittest.py b/testing/mozharness/scripts/desktop_unittest.py index ea8ff30d625b..7674890632bf 100755 --- a/testing/mozharness/scripts/desktop_unittest.py +++ b/testing/mozharness/scripts/desktop_unittest.py @@ -894,7 +894,7 @@ class DesktopUnittest(TestingMixin, MercurialScript, MozbaseMixin, # Per-test reset/dump is only supported for xpcshell and # Linux for the time being. if not is_baseline_test and suite == 'xpcshell' and self._is_linux(): - env['GCOV_RESULTS_DIR'] = gcov_dir = tempfile.mkdtemp() + env['GCOV_RESULTS_DIR'] = tempfile.mkdtemp() return_code = self.run_command(final_cmd, cwd=dirs['abs_work_dir'], output_timeout=cmd_timeout, @@ -903,8 +903,13 @@ class DesktopUnittest(TestingMixin, MercurialScript, MozbaseMixin, if self.per_test_coverage: self.add_per_test_coverage_report( - gcov_dir, jsvm_dir, suite, per_test_args[-1] + env['GCOV_RESULTS_DIR'] if 'GCOV_RESULTS_DIR' in env else gcov_dir, + jsvm_dir, + suite, + per_test_args[-1] ) + if 'GCOV_RESULTS_DIR' in env: + shutil.rmtree(gcov_dir) # mochitest, reftest, and xpcshell suites do not return # appropriate return codes. Therefore, we must parse the output diff --git a/testing/xpcshell/head.js b/testing/xpcshell/head.js index 9c1a813c73e5..3a32b7f852f0 100644 --- a/testing/xpcshell/head.js +++ b/testing/xpcshell/head.js @@ -511,21 +511,9 @@ function _execute_test() { this[func] = Assert[func].bind(Assert); } - let perTestCoverageEnabled = false; - try { - ChromeUtils.import("resource://testing-common/PerTestCoverageUtils.jsm"); - perTestCoverageEnabled = true; - } catch (e) { - // If the module doesn't exist, code coverage is disabled. - // Otherwise, rethrow the exception. - if (e.result != Cr.NS_ERROR_FILE_NOT_FOUND) { - throw e; - } - } + const {PerTestCoverageUtils} = ChromeUtils.import("resource://testing-common/PerTestCoverageUtils.jsm", {}); - if (perTestCoverageEnabled) { - PerTestCoverageUtils.beforeTest(); - } + PerTestCoverageUtils.beforeTestSync(); try { do_test_pending("MAIN run_test"); @@ -546,9 +534,7 @@ function _execute_test() { coverageCollector.recordTestCoverage(_TEST_FILE[0]); } - if (perTestCoverageEnabled) { - PerTestCoverageUtils.afterTest(); - } + PerTestCoverageUtils.afterTestSync(); } catch (e) { _passed = false; // do_check failures are already logged and set _quit to true and throw diff --git a/toolkit/components/downloads/DownloadIntegration.jsm b/toolkit/components/downloads/DownloadIntegration.jsm index 1649eb71ed3e..ba240f4d1ed2 100644 --- a/toolkit/components/downloads/DownloadIntegration.jsm +++ b/toolkit/components/downloads/DownloadIntegration.jsm @@ -595,7 +595,7 @@ var DownloadIntegration = { } let isWindowsExe = AppConstants.platform == "win" && - fileExtension.toLowerCase() == "exe"; + fileExtension && fileExtension.toLowerCase() == "exe"; // Ask for confirmation if the file is executable, except for .exe on // Windows where the operating system will show the prompt based on the diff --git a/toolkit/mozapps/extensions/gen_built_in_addons.py b/toolkit/mozapps/extensions/gen_built_in_addons.py new file mode 100644 index 000000000000..416f0d52dd18 --- /dev/null +++ b/toolkit/mozapps/extensions/gen_built_in_addons.py @@ -0,0 +1,94 @@ +# 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/. + +from __future__ import absolute_import, print_function, unicode_literals + +import argparse +import json +import os.path +import sys + +import buildconfig +import mozpack.path as mozpath + +from mozpack.copier import FileRegistry +from mozpack.manifests import InstallManifest + + +# A list of build manifests, and their relative base paths, from which to +# extract lists of install files. These vary depending on which backend we're +# using, so nonexistent manifests are ignored. +manifest_paths = ( + ('', '_build_manifests/install/dist_bin'), + ('', 'faster/install_dist_bin'), + ('browser', 'faster/install_dist_bin_browser'), +) + + +def get_registry(paths): + used_paths = set() + + registry = FileRegistry() + for base, path in paths: + full_path = mozpath.join(buildconfig.topobjdir, path) + if not os.path.exists(full_path): + continue + + used_paths.add(full_path) + + reg = FileRegistry() + InstallManifest(full_path).populate_registry(reg) + + for p, f in reg: + path = mozpath.join(base, p) + if not registry.contains(path): + registry.add(path, f) + + return registry, used_paths + + +def get_child(base, path): + """Returns the nearest parent of `path` which is an immediate child of + `base`""" + + dirname = mozpath.dirname(path) + while dirname != base: + path = dirname + dirname = mozpath.dirname(path) + return path + + +def main(output, *args): + parser = argparse.ArgumentParser( + description='Produces a JSON manifest of built-in add-ons') + parser.add_argument('--features', type=str, dest='featuresdir', + action='store', help=('The distribution sub-directory ' + 'containing feature add-ons')) + args = parser.parse_args(args) + + registry, inputs = get_registry(manifest_paths) + + dicts = {} + for path in registry.match('dictionaries/*.dic'): + base, ext = os.path.splitext(mozpath.basename(path)) + dicts[base] = path + + listing = { + "dictionaries": dicts, + } + + if args.featuresdir: + features = set() + for p in registry.match('%s/*' % args.featuresdir): + features.add(mozpath.basename(get_child(args.featuresdir, p))) + + listing["system"] = sorted(features) + + json.dump(listing, output) + + return inputs + + +if __name__ == '__main__': + main(sys.stdout, *sys.argv[1:]) diff --git a/toolkit/mozapps/extensions/moz.build b/toolkit/mozapps/extensions/moz.build index d9714ab2c454..ee2cc3d8a766 100644 --- a/toolkit/mozapps/extensions/moz.build +++ b/toolkit/mozapps/extensions/moz.build @@ -25,6 +25,24 @@ XPIDL_SOURCES += [ XPIDL_MODULE = 'extensions' +built_in_addons = 'built_in_addons.json' +GENERATED_FILES += [built_in_addons] +manifest = GENERATED_FILES[built_in_addons] +manifest.script = 'gen_built_in_addons.py' + +if CONFIG['MOZ_BUILD_APP'] == 'browser': + manifest.flags = ['--features=browser/features'] + + FINAL_TARGET_FILES.browser.chrome.browser.content.browser += [ + '!%s' % built_in_addons, + ] +elif CONFIG['MOZ_BUILD_APP'] == 'mobile/android': + manifest.flags = ['--features=features'] + + FINAL_TARGET_FILES.chrome.chrome.content += [ + '!%s' % built_in_addons, + ] + EXTRA_COMPONENTS += [ 'addonManager.js', 'amContentHandler.js', diff --git a/tools/code-coverage/PerTestCoverageUtils.jsm b/tools/code-coverage/PerTestCoverageUtils.jsm index c804aaa89b00..b82b00bd3cad 100644 --- a/tools/code-coverage/PerTestCoverageUtils.jsm +++ b/tools/code-coverage/PerTestCoverageUtils.jsm @@ -10,55 +10,66 @@ var EXPORTED_SYMBOLS = ["PerTestCoverageUtils"]; ChromeUtils.defineModuleGetter(this, "Services", "resource://gre/modules/Services.jsm"); -class PerTestCoverageUtilsClass { - constructor(tmp_gcov_dir, gcov_dir) { - this.tmp_gcov_dir = tmp_gcov_dir; - this.gcov_dir = gcov_dir; - - this.codeCoverageService = Cc["@mozilla.org/tools/code-coverage;1"].getService(Ci.nsICodeCoverage); - } - - _awaitPromise(promise) { - let ret; - let complete = false; - let error = null; - promise.catch(e => error = e).then(v => { - ret = v; - complete = true; - }); - Services.tm.spinEventLoopUntil(() => complete); - if (error) { - throw new Error(error); - } - return ret; - } - - // Resets the counters to 0. - beforeTest() { - this._awaitPromise(this.codeCoverageService.resetCounters()); - } - - // Dumps counters and moves the gcda files in the directory expected by codecoverage.py. - afterTest() { - this._awaitPromise(this.codeCoverageService.dumpCounters()); - - let srcDir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); - srcDir.initWithPath(this.tmp_gcov_dir); - - let destDir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); - destDir.initWithPath(this.gcov_dir); - - let srcDirEntries = srcDir.directoryEntries; - while (srcDirEntries.hasMoreElements()) { - srcDirEntries.nextFile.moveTo(destDir, null); - } - } -} - const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment); // This is the directory where gcov is emitting the gcda files. const tmp_gcov_dir = env.get("GCOV_PREFIX"); // This is the directory where codecoverage.py is expecting to see the gcda files. const gcov_dir = env.get("GCOV_RESULTS_DIR"); -const PerTestCoverageUtils = gcov_dir ? new PerTestCoverageUtilsClass(tmp_gcov_dir, gcov_dir) : null; +const enabled = !!gcov_dir; + +function awaitPromise(promise) { + let ret; + let complete = false; + let error = null; + promise.catch(e => error = e).then(v => { + ret = v; + complete = true; + }); + Services.tm.spinEventLoopUntil(() => complete); + if (error) { + throw new Error(error); + } + return ret; +} + +var PerTestCoverageUtils = class PerTestCoverageUtilsClass { + // Resets the counters to 0. + static async beforeTest() { + if (!enabled) { + return; + } + + let codeCoverageService = Cc["@mozilla.org/tools/code-coverage;1"].getService(Ci.nsICodeCoverage); + await codeCoverageService.resetCounters(); + } + + static beforeTestSync() { + awaitPromise(this.beforeTest()); + } + + // Dumps counters and moves the gcda files in the directory expected by codecoverage.py. + static async afterTest() { + if (!enabled) { + return; + } + + let codeCoverageService = Cc["@mozilla.org/tools/code-coverage;1"].getService(Ci.nsICodeCoverage); + await codeCoverageService.dumpCounters(); + + let srcDir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); + srcDir.initWithPath(tmp_gcov_dir); + + let destDir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); + destDir.initWithPath(gcov_dir); + + let srcDirEntries = srcDir.directoryEntries; + while (srcDirEntries.hasMoreElements()) { + srcDirEntries.nextFile.moveTo(destDir, null); + } + } + + static afterTestSync() { + awaitPromise(this.afterTest()); + } +}; diff --git a/tools/code-coverage/moz.build b/tools/code-coverage/moz.build index d66699a740a1..103899924b43 100644 --- a/tools/code-coverage/moz.build +++ b/tools/code-coverage/moz.build @@ -4,6 +4,8 @@ # 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/. +TESTING_JS_MODULES += ['PerTestCoverageUtils.jsm'] + if CONFIG['MOZ_CODE_COVERAGE']: XPIDL_MODULE = 'code-coverage' @@ -31,6 +33,4 @@ if CONFIG['MOZ_CODE_COVERAGE']: XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini'] MOCHITEST_MANIFESTS += ['tests/mochitest/mochitest.ini'] - TESTING_JS_MODULES += ['PerTestCoverageUtils.jsm'] - FINAL_LIBRARY = 'xul' diff --git a/tools/code-coverage/tests/xpcshell/test_basic.js b/tools/code-coverage/tests/xpcshell/test_basic.js index 5fe36a5948b7..f584ed30fb08 100644 --- a/tools/code-coverage/tests/xpcshell/test_basic.js +++ b/tools/code-coverage/tests/xpcshell/test_basic.js @@ -2,7 +2,9 @@ * 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 run_test() { +async function run_test() { + do_test_pending(); + Assert.ok("@mozilla.org/tools/code-coverage;1" in Cc); let codeCoverageCc = Cc["@mozilla.org/tools/code-coverage;1"]; @@ -11,7 +13,9 @@ function run_test() { let codeCoverage = codeCoverageCc.getService(Ci.nsICodeCoverage); Assert.ok(!!codeCoverage); - codeCoverage.dumpCounters(); + await codeCoverage.dumpCounters(); - codeCoverage.resetCounters(); + await codeCoverage.resetCounters(); + + do_test_finished(); } diff --git a/tools/code-coverage/tests/xpcshell/test_basic_child_and_parent.js b/tools/code-coverage/tests/xpcshell/test_basic_child_and_parent.js index 88161f197c9b..8cac17844b71 100644 --- a/tools/code-coverage/tests/xpcshell/test_basic_child_and_parent.js +++ b/tools/code-coverage/tests/xpcshell/test_basic_child_and_parent.js @@ -6,11 +6,11 @@ function run_test() { do_load_child_test_harness(); do_test_pending(); - sendCommand("let v = 'test';", function() { + sendCommand("let v = 'test';", async function() { let codeCoverage = Cc["@mozilla.org/tools/code-coverage;1"].getService(Ci.nsICodeCoverage); - codeCoverage.dumpCounters(); - codeCoverage.resetCounters(); + await codeCoverage.dumpCounters(); + await codeCoverage.resetCounters(); do_test_finished(); }); diff --git a/widget/cocoa/nsCocoaFeatures.h b/widget/cocoa/nsCocoaFeatures.h index a6a60e26bbdb..c823a5f55463 100644 --- a/widget/cocoa/nsCocoaFeatures.h +++ b/widget/cocoa/nsCocoaFeatures.h @@ -20,9 +20,10 @@ public: static int32_t OSXVersionBugFix(); static bool OnYosemiteOrLater(); static bool OnElCapitanOrLater(); + static bool OnSierraExactly(); static bool OnSierraOrLater(); static bool OnHighSierraOrLater(); - static bool OnSierraExactly(); + static bool OnMojaveOrLater(); static bool IsAtLeastVersion(int32_t aMajor, int32_t aMinor, int32_t aBugFix=0); diff --git a/widget/cocoa/nsCocoaFeatures.mm b/widget/cocoa/nsCocoaFeatures.mm index 17e33d342242..1022264a7d05 100644 --- a/widget/cocoa/nsCocoaFeatures.mm +++ b/widget/cocoa/nsCocoaFeatures.mm @@ -20,6 +20,7 @@ #define MAC_OS_X_VERSION_10_11_HEX 0x000010B0 #define MAC_OS_X_VERSION_10_12_HEX 0x000010C0 #define MAC_OS_X_VERSION_10_13_HEX 0x000010D0 +#define MAC_OS_X_VERSION_10_14_HEX 0x000010E0 #include "nsCocoaFeatures.h" #include "nsCocoaUtils.h" @@ -162,18 +163,6 @@ nsCocoaFeatures::OnElCapitanOrLater() return (OSXVersion() >= MAC_OS_X_VERSION_10_11_HEX); } -/* static */ bool -nsCocoaFeatures::OnSierraOrLater() -{ - return (OSXVersion() >= MAC_OS_X_VERSION_10_12_HEX); -} - -/* static */ bool -nsCocoaFeatures::OnHighSierraOrLater() -{ - return (OSXVersion() >= MAC_OS_X_VERSION_10_13_HEX); -} - /* static */ bool nsCocoaFeatures::OnSierraExactly() { @@ -188,12 +177,30 @@ Gecko_OnSierraExactly() return nsCocoaFeatures::OnSierraExactly(); } +/* static */ bool +nsCocoaFeatures::OnSierraOrLater() +{ + return (OSXVersion() >= MAC_OS_X_VERSION_10_12_HEX); +} + +/* static */ bool +nsCocoaFeatures::OnHighSierraOrLater() +{ + return (OSXVersion() >= MAC_OS_X_VERSION_10_13_HEX); +} + bool Gecko_OnHighSierraOrLater() { return nsCocoaFeatures::OnHighSierraOrLater(); } +/* static */ bool +nsCocoaFeatures::OnMojaveOrLater() +{ + return (OSXVersion() >= MAC_OS_X_VERSION_10_14_HEX); +} + /* static */ bool nsCocoaFeatures::IsAtLeastVersion(int32_t aMajor, int32_t aMinor, int32_t aBugFix) { diff --git a/widget/cocoa/nsLookAndFeel.mm b/widget/cocoa/nsLookAndFeel.mm index 3d88ae779d80..0291396795cb 100644 --- a/widget/cocoa/nsLookAndFeel.mm +++ b/widget/cocoa/nsLookAndFeel.mm @@ -594,10 +594,13 @@ bool nsLookAndFeel::AllowOverlayScrollbarsOverlap() bool nsLookAndFeel::SystemWantsDarkTheme() { - // This returns true if the macOS system appearance is set to dark mode, false - // otherwise. - return !![[NSUserDefaults standardUserDefaults] - stringForKey:@"AppleInterfaceStyle"]; + // This returns true if the macOS system appearance is set to dark mode on + // 10.14+, false otherwise. + if (nsCocoaFeatures::OnMojaveOrLater()) { + return !![[NSUserDefaults standardUserDefaults] + stringForKey:@"AppleInterfaceStyle"]; + } + return false; } bool