diff --git a/browser/config/mozconfigs/linux64/stylo-only b/browser/config/mozconfigs/linux64/stylo-only new file mode 100644 index 000000000000..2e2394a21170 --- /dev/null +++ b/browser/config/mozconfigs/linux64/stylo-only @@ -0,0 +1,3 @@ +. "$topsrcdir/browser/config/mozconfigs/linux64/nightly" + +ac_add_options --enable-stylo=only diff --git a/browser/config/mozconfigs/linux64/stylo-only-debug b/browser/config/mozconfigs/linux64/stylo-only-debug new file mode 100644 index 000000000000..61e8fec3b41e --- /dev/null +++ b/browser/config/mozconfigs/linux64/stylo-only-debug @@ -0,0 +1,3 @@ +. "$topsrcdir/browser/config/mozconfigs/linux64/debug" + +ac_add_options --enable-stylo=only diff --git a/build/Makefile.in b/build/Makefile.in deleted file mode 100644 index 2f284c253e24..000000000000 --- a/build/Makefile.in +++ /dev/null @@ -1,20 +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/. - -ifdef MOZTTDIR -# Install the Firefox OS fonts. -include $(MOZTTDIR)/fonts.mk -MOZTT_DEST = $(FINAL_TARGET)/fonts -MOZTT_FILES = $(patsubst external/moztt/%,$(MOZTTDIR)/%,$(filter external/moztt/%,$(subst :, ,$(PRODUCT_COPY_FILES)))) -INSTALL_TARGETS += MOZTT -endif - -include $(topsrcdir)/config/rules.mk - -TARGET_DEPTH = .. -include $(srcdir)/automation-build.mk - -libs:: automation.py - diff --git a/config/makefiles/test/Makefile.in b/config/makefiles/test/Makefile.in deleted file mode 100644 index 6c66d820e900..000000000000 --- a/config/makefiles/test/Makefile.in +++ /dev/null @@ -1,98 +0,0 @@ -# -*- makefile -*- -# -# 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/. - -STANDALONE_MAKEFILE := 1 -USE_AUTOTARGETS_MK = 1 -MAKEUTILS_UNIT_TEST = 1 -include $(topsrcdir)/config/makefiles/makeutils.mk - -dir-ts = .deps/test -check-arglist = $(dir-ts)/arglist.ts -check-autotargets = $(dir-ts)/autotargets_mk.ts -check-XinY = $(dir-ts)/check_XinY_mk.ts -check-tests =\ - $(check-arglist) \ - $(check-autotargets) \ - $(check-XinY) \ - $(NULL) - - -##------------------_## -##---] TARGETS [---## -##------------------_## -all:: - -clean: - $(RM) $(check-tests) - -########################################################################### -## Logic processed at compile time so be selective about when to test -## $(MAKE) check VERBOSE=1 -ifneq ($(NULL),$(findstring check,$(MAKECMDGOALS))) # - -check-preqs =\ - $(call mkdir_deps,$(dir-ts)) \ - $(check-tests) \ - $(NULL) - -check:: $(check-preqs) - @true - - -ifdef VERBOSE #{ gmake check VERBOSE=1 - $(info ===========================================================================) - $(info Running test: $(MAKECMDGOALS): pwd=$(CURDIR)) - $(info ===========================================================================) -endif #} - -ifndef requiredfunction - $(error requiredfunction is not defined) -endif - - -################## -check-XinY-preqs=\ - $(call mkdir_deps,$(dir-ts)) \ - $(topsrcdir)/config/makefiles/makeutils.mk \ - $(srcdir)/check_XinY.mk \ - $(eval include $(srcdir)/check_XinY.mk) \ - $(NULL) - -$(check-XinY): $(check-XinY-preqs) - @$(TOUCH) $@ -# - - -########################################################################### -## check-arglist.mk always invoked as a compile time test -## maintain real file dependencies for use later on. -check-arglist-preqs=\ - $(call mkdir_deps,$(dir-ts)) \ - $(topsrcdir)/config/makefiles/makeutils.mk \ - $(srcdir)/check-arglist.mk \ - $(eval include $(srcdir)/check-arglist.mk) \ - $(NULL) - -$(check-arglist): $(check-arglist-preqs) - @$(TOUCH) $@ -# - - -########################################################################### -# -check-autotargets-preqs=\ - $(call mkdir_deps,$(dir-ts)) \ - $(topsrcdir)/config/makefiles/makeutils.mk \ - $(topsrcdir)/config/makefiles/autotargets.mk \ - $(srcdir)/check-autotargets.mk \ - $(eval include $(srcdir)/check-autotargets.mk) \ - $(NULL) - -$(check-autotargets): $(check-autotargets-preqs) - @$(TOUCH) $@ -# - -endif #} findstring MAKECMDGOAL diff --git a/config/makefiles/test/check-arglist.mk b/config/makefiles/test/check-arglist.mk deleted file mode 100644 index fb720ee42c0e..000000000000 --- a/config/makefiles/test/check-arglist.mk +++ /dev/null @@ -1,100 +0,0 @@ -# -*- makefile -*- -# -# 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/. - -ifdef VERBOSE - $(warning loading test) -endif - - -$(call requiredfunction,getargv) -$(call requiredfunction,subargv) -$(call requiredfunction,istype isval isvar) - -# arg_scalar = [scalar|literal] -arg_list = foo bar -arg_ref = arg_list - -## Identify type of function argument(s) -######################################## -ifneq (scalar,$(call istype,arg_scalar)) - $(error istype(arg_scalar)=scalar, found [$(call istype,arg_scalar)]) -endif -ifneq (list,$(call istype,arg_list)) - $(error istype(arg_list)=list, found [$(call istype,arg_list)]) -endif -ifneq (list,$(call istype,arg_ref)) - $(error istype(arg_ref)=list, found [$(call istype,arg_ref)]) -endif - -## Type == scalar or a list of values -##################################### -ifneq (true,$(call isval,scalar)) - $(error isval(scalar)=true, found [$(call isval,scalar)]) -endif -ifneq ($(NULL),$(call isval,arg_list)) - $(error isval(arg_list)=null, found [$(call isval,arg_list)]) -endif - -## type == reference: macro=>macro => $($(1)) -############################################# -ifneq ($(NULL),$(call isvar,scalar)) - $(error isvar(scalar)=$(NULL), found [$(call isvar,scalar)]) -endif -ifneq (true,$(call isvar,arg_list)) - $(error isvar(arg_list)=true, found [$(call isvar,arg_list)]) -endif -ifneq (true,$(call isvar,arg_ref)) - $(error isvar(arg_ref)=true, found [$(call isvar,arg_ref)]) -endif - -# Verify getargv expansion -########################## -ifneq (scalar,$(call getargv,scalar)) - $(error getargv(scalar)=scalar, found [$(call getargv,scalar)]) -endif -ifneq ($(arg_list),$(call getargv,arg_list)) - $(error getargv(arg_list)=list, found [$(call getargv,arg_list)]) -endif -ifneq (arg_list,$(call getargv,arg_ref)) - $(error getargv(arg_ref)=list, found [$(call getargv,arg_ref)]) -endif - -########################################################################### -## -########################################################################### -ifdef MANUAL_TEST #{ - # For automated testing a callback is needed that can set an external status - # variable that can be tested. Syntax is tricky to get correct functionality. - ifdef VERBOSE - $(info ) - $(info ===========================================================================) - $(info Running test: checkIfEmpty) - $(info ===========================================================================) - endif - - #status = - #setTRUE =status=true - #setFALSE =status=$(NULL) - #$(call checkIfEmpty,setFALSE NULL) - #$(if $(status),$(error checkIfEmpty(xyz) failed)) - #$(call checkIfEmpty,setTRUE xyz) - #$(if $(status),$(error checkIfEmpty(xyz) failed)) - xyz=abc - $(info STATUS: warnIfEmpty - two vars) - $(call warnIfEmpty,foo xyz bar) - $(info STATUS: errorIfEmpty - on first var) - $(call errorIfEmpty,foo xyz bar) - $(error TEST FAILED: processing should not reach this point) -endif #} - -# Verify subargv expansion -########################## -subargs=foo bar tans fans -subargs_exp=tans fans -subargs_found=$(call subargv,4,$(subargs)) -ifneq ($(subargs_exp),$(subargs_found)) - $(error subargv(4,$(subargs)): expected [$(subargs_exp)] found [$(subargs_found)]) -endif diff --git a/config/makefiles/test/check-autotargets.mk b/config/makefiles/test/check-autotargets.mk deleted file mode 100644 index c103f1d02dbe..000000000000 --- a/config/makefiles/test/check-autotargets.mk +++ /dev/null @@ -1,84 +0,0 @@ -# -*- makefile -*- -# -# 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/. - -ifdef VERBOSE - $(warning loading test) -endif - -space=$(null) $(null) -GENERATED_DIRS = bogus # test data - -undefine USE_AUTOTARGETS_MK -undefine INCLUDED_AUTOTARGETS_MK -include $(topsrcdir)/config/makefiles/autotargets.mk - -ifndef INCLUDED_AUTOTARGETS_MK - $(error autotargets.mk was not included -endif - -$(call requiredfunction,mkdir_deps) - - -# Verify test data populated makefile vars correctly -vars = AUTO_DEPS GARBAGE_DIRS GENERATED_DIRS_DEPS -$(foreach var,$(vars),$(call errorIfEmpty,$(var))) - -# Data should also be valid -ifneq (bogus,$(findstring bogus,$(AUTO_DEPS))) - $(error AUTO_DEPS=[$(AUTO_DEPS)] is not set correctly) -endif - - -# relpath -path := foo/bar.c -exp := foo/.mkdir.done -found := $(call mkdir_deps,$(dir $(path))) -ifneq ($(exp),$(found)) - $(error mkdir_deps($(path))=$(exp) not set correctly [$(found)]) -endif - -# abspath -path := /foo//bar/ -exp := /foo/bar/.mkdir.done -found := $(call mkdir_deps,$(path)) -ifneq ($(exp),$(found)) - $(error mkdir_deps($(path))=$(exp) not set correctly [$(found)]) -endif - - -## verify strip_slash -##################### - -path := a/b//c///d////e///// -exp := a/b/c/d/e/.mkdir.done -found := $(call mkdir_deps,$(path)) -ifneq ($(exp),$(found)) - $(error mkdir_deps($(path))=$(exp) not set correctly [$(found)]) -endif - - -## verify mkdir_stem() -###################### -path := verify/mkdir_stem -pathD = $(call mkdir_deps,$(path)) -pathS = $(call mkdir_stem,$(pathD)) -exp := $(path) - -ifeq ($(pathD),$(pathS)) - $(error mkdir_deps and mkdir_stem should not match [$(pathD)]) -endif -ifneq ($(pathS),$(exp)) - $(error mkdir_stem=[$(pathS)] != exp=[$(exp)]) -endif - - -## Verify embedded whitespace has been protected -path := a/b$(space)c//d -exp := a/b$(space)c/d -found := $(call slash_strip,$(path)) -ifneq ($(exp),$(found)) - $(error slash_strip($(path))=$(exp) not set correctly [$(found)]) -endif diff --git a/config/makefiles/test/check_XinY.mk b/config/makefiles/test/check_XinY.mk deleted file mode 100644 index 1e7a19f0e747..000000000000 --- a/config/makefiles/test/check_XinY.mk +++ /dev/null @@ -1,70 +0,0 @@ -# -*- makefile -*- -# -# 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/. - -# Verify dependencies are available -$(call requiredfunction,getargv subargv is_XinY errorifneq) - -ifdef VERBOSE - $(warning loading test) -endif - -zero := 0 -one := 1 - -# Verify 'invalid' is not matched -val := invalid -$(call errorifneq,$(zero),$(words $(call is_XinY,foo,$(val)))) -$(call errorifneq,$(zero),$(words $(call is_XinY,clean,$(val)))) -$(call errorifneq,$(zero),$(words $(call is_XinY,clean%,$(val)))) - -# verify strcmp('clean') -val := clean -$(call errorifneq,$(zero),$(words $(call is_XinY,foo,$(val)))) -$(call errorifneq,$(one),$(words $(call is_XinY,clean,$(val)))) -$(call errorifneq,$(one),$(words $(call is_XinY,clean%,$(val)))) -$(call errorifneq,$(one),$(words $(call is_XinY,%clean,$(val)))) - -# List match for 'clean' -val := blah clean distclean FcleanG clean-level-1 -wanted := clean distclean clean-level-1 -$(call errorifneq,$(zero),$(words $(call is_XinY_debug,foo,$(val)))) -$(call errorifneq,$(one),$(words $(call is_XinY,clean,$(val)))) -$(call errorifneq,$(one),$(words $(call is_XinY,distclean,$(val)))) - -# pattern match 'clean' -# match: clean, distclean, clean-level-1 -# exclude: FcleanG -TEST_MAKECMDGOALS := $(val) -$(call errorifneq,3,$(words $(call isTargetStemClean))) - -TEST_MAKECMDGOALS := invalid -$(call errorifneq,$(zero),$(words $(call isTargetStemClean))) - - -############################# -ifdef VERBOSE - $(call banner,Unit test: isTargetStem) -endif - -# Verify list argument processing -TEST_MAKECMDGOALS := echo -$(call errorifneq,$(one),$(words $(call isTargetStem,echo,show))) - -TEST_MAKECMDGOALS := echo-123 -$(call errorifneq,$(one),$(words $(call isTargetStem,echo,show))) - -TEST_MAKECMDGOALS := show -$(call errorifneq,$(one),$(words $(call isTargetStem,echo,show))) - -TEST_MAKECMDGOALS := show-123 -$(call errorifneq,$(one),$(words $(call isTargetStem,echo,show))) - -TEST_MAKECMDGOALS := show-123-echo -$(call errorifneq,$(one),$(words $(call isTargetStem,echo,show))) - -TEST_MAKECMDGOALS := invalid -$(call errorifneq,$(zero),$(words $(call isTargetStem,echo,show))) - diff --git a/config/makefiles/test/moz.build b/config/makefiles/test/moz.build deleted file mode 100644 index 28919c271d33..000000000000 --- a/config/makefiles/test/moz.build +++ /dev/null @@ -1,6 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - diff --git a/config/moz.build b/config/moz.build index 7bac5e6fe993..0108b977bff8 100644 --- a/config/moz.build +++ b/config/moz.build @@ -16,7 +16,6 @@ DIST_INSTALL = False NoVisibilityFlags() CONFIGURE_SUBST_FILES += [ - 'makefiles/test/Makefile', 'tests/src-simple/Makefile', ] diff --git a/devtools/client/debugger/new/README.mozilla b/devtools/client/debugger/new/README.mozilla index fb037fa31b08..a7f074d21986 100644 --- a/devtools/client/debugger/new/README.mozilla +++ b/devtools/client/debugger/new/README.mozilla @@ -1,8 +1,8 @@ This is the debugger.html project output. See https://github.com/devtools-html/debugger.html -Version 18.0 -Comparison: https://github.com/devtools-html/debugger.html/compare/release-17...release-18 +Version 19.0 +Comparison: https://github.com/devtools-html/debugger.html/compare/release-18...release-19 Packages: - babel-plugin-transform-es2015-modules-commonjs @6.26.0 diff --git a/devtools/client/debugger/new/debugger.css b/devtools/client/debugger/new/debugger.css index ff31a07fed06..241a8cea38a2 100644 --- a/devtools/client/debugger/new/debugger.css +++ b/devtools/client/debugger/new/debugger.css @@ -1928,6 +1928,9 @@ html .toggle-button.end.vertical svg { .source-footer .mapped-source { color: var(--theme-body-color); padding: 2.5px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } /* 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 diff --git a/devtools/client/debugger/new/debugger.js b/devtools/client/debugger/new/debugger.js index 7f60dd630b04..c74c61448829 100644 --- a/devtools/client/debugger/new/debugger.js +++ b/devtools/client/debugger/new/debugger.js @@ -10102,7 +10102,7 @@ const prefs = new PrefsHelper("devtools", { fileSearchWholeWord: ["Bool", "debugger.file-search-whole-word"], fileSearchRegexMatch: ["Bool", "debugger.file-search-regex-match"], debuggerPrefsSchemaVersion: ["Char", "debugger.prefs-schema-version"], - projectDirectoryRoot: ["Char", "project-directory-root", ""] + projectDirectoryRoot: ["Char", "debugger.project-directory-root", ""] }); /* harmony export (immutable) */ __webpack_exports__["prefs"] = prefs; @@ -17921,7 +17921,7 @@ function createPendingBreakpoint(bp) { Object.defineProperty(exports, "__esModule", { value: true }); -exports.replaceOriginalVariableName = exports.getFramework = exports.hasSyntaxError = exports.clearSources = exports.setSource = exports.hasSource = exports.getEmptyLines = exports.getNextStep = exports.clearASTs = exports.clearScopes = exports.clearSymbols = exports.findOutOfScopeLocations = exports.getVariablesInScope = exports.getScopes = exports.getSymbols = exports.getClosestExpression = exports.stopParserWorker = exports.startParserWorker = undefined; +exports.replaceOriginalVariableName = exports.getFramework = exports.hasSyntaxError = exports.clearSources = exports.setSource = exports.hasSource = exports.getEmptyLines = exports.isInvalidPauseLocation = exports.getNextStep = exports.clearASTs = exports.clearScopes = exports.clearSymbols = exports.findOutOfScopeLocations = exports.getVariablesInScope = exports.getScopes = exports.getSymbols = exports.getClosestExpression = exports.stopParserWorker = exports.startParserWorker = undefined; var _devtoolsUtils = __webpack_require__(1363); @@ -17942,6 +17942,7 @@ const clearSymbols = exports.clearSymbols = dispatcher.task("clearSymbols"); const clearScopes = exports.clearScopes = dispatcher.task("clearScopes"); const clearASTs = exports.clearASTs = dispatcher.task("clearASTs"); const getNextStep = exports.getNextStep = dispatcher.task("getNextStep"); +const isInvalidPauseLocation = exports.isInvalidPauseLocation = dispatcher.task("isInvalidPauseLocation"); const getEmptyLines = exports.getEmptyLines = dispatcher.task("getEmptyLines"); const hasSource = exports.hasSource = dispatcher.task("hasSource"); const setSource = exports.setSource = dispatcher.task("setSource"); @@ -19160,6 +19161,16 @@ const arrowBtn = (onClick, type, className, tooltip) => { class SearchInput extends _react.Component { componentDidMount() { + this.setFocus(); + } + + componentDidUpdate(prevProps) { + if (this.props.shouldFocus && !prevProps.shouldFocus) { + this.setFocus(); + } + } + + setFocus() { if (this.$input) { const input = this.$input; input.focus(); @@ -19953,15 +19964,15 @@ var _selectors = __webpack_require__(1352); var _ui = __webpack_require__(1421); -/* 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 . */ +var _source = __webpack_require__(1356); function setContextMenu(type, event) { return ({ dispatch }) => { dispatch({ type: "SET_CONTEXT_MENU", contextMenu: { type, event } }); }; -} +} /* 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 setPrimaryPaneTab(tabName) { return { type: "SET_PRIMARY_PANE_TAB", tabName }; @@ -19981,6 +19992,10 @@ function setActiveSearch(activeSearch) { return; } + if ((0, _selectors.getQuickOpenEnabled)(getState())) { + dispatch({ type: "CLOSE_QUICK_OPEN" }); + } + dispatch({ type: "TOGGLE_ACTIVE_SEARCH", value: activeSearch @@ -20009,7 +20024,7 @@ function showSource(sourceId) { dispatch({ type: "SHOW_SOURCE", - sourceUrl: source.get("url") + sourceUrl: (0, _source.getRawSourceURL)(source.get("url")) }); }; } @@ -20330,6 +20345,7 @@ var _extends = Object.assign || function (target) { for (var i = 1; i < argument exports.getPauseReason = getPauseReason; exports.isStepping = isStepping; exports.isPaused = isPaused; +exports.getPreviousPauseFrameLocation = getPreviousPauseFrameLocation; exports.isEvaluatingExpression = isEvaluatingExpression; exports.getPopupObjectProperties = getPopupObjectProperties; exports.getIsWaitingOnBreak = getIsWaitingOnBreak; @@ -20368,7 +20384,8 @@ const createPauseState = exports.createPauseState = () => ({ shouldIgnoreCaughtExceptions: _prefs.prefs.ignoreCaughtExceptions, canRewind: false, debuggeeUrl: "", - command: "" + command: "", + previousLocation: null }); const emptyPauseState = { @@ -20379,7 +20396,8 @@ const emptyPauseState = { original: {} }, selectedFrameId: null, - loadedObjects: {} + loadedObjects: {}, + previousLocation: null }; function update(state = createPauseState(), action) { @@ -20485,7 +20503,12 @@ function update(state = createPauseState(), action) { }); case "COMMAND": - return action.status === "start" ? _extends({}, state, emptyPauseState, { command: action.command }) : _extends({}, state, { command: "" }); + { + return action.status === "start" ? _extends({}, state, emptyPauseState, { + command: action.command, + previousLocation: buildPreviousLocation(state, action) + }) : _extends({}, state, { command: "" }); + } case "RESUME": // We clear why on resume because we need it to decide if @@ -20504,6 +20527,24 @@ function update(state = createPauseState(), action) { return state; } +function buildPreviousLocation(state, action) { + const { frames, previousLocation } = state; + + if (action.command !== "stepOver") { + return null; + } + + const frame = frames && frames.length > 0 ? frames[0] : null; + if (!frame) { + return previousLocation; + } + + return { + location: frame.location, + generatedLocation: frame.generatedLocation + }; +} + // Selectors // Unfortunately, it's really hard to make these functions accept just @@ -20531,6 +20572,10 @@ function isPaused(state) { return !!getFrames(state); } +function getPreviousPauseFrameLocation(state) { + return state.pause.previousLocation; +} + function isEvaluatingExpression(state) { return state.pause.command === "expression"; } @@ -21329,74 +21374,7 @@ function setOutOfScopeLocations() { } /***/ }), -/* 1400 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.getPauseReason = getPauseReason; -exports.isException = isException; -exports.isInterrupted = isInterrupted; -exports.inDebuggerEval = inDebuggerEval; - - -// Map protocol pause "why" reason to a valid L10N key -// These are the known unhandled reasons: -// "breakpointConditionThrown", "clientEvaluated" -// "interrupted", "attached" -const reasons = { - debuggerStatement: "whyPaused.debuggerStatement", - breakpoint: "whyPaused.breakpoint", - exception: "whyPaused.exception", - resumeLimit: "whyPaused.resumeLimit", - pauseOnDOMEvents: "whyPaused.pauseOnDOMEvents", - breakpointConditionThrown: "whyPaused.breakpointConditionThrown", - - // V8 - DOM: "whyPaused.breakpoint", - EventListener: "whyPaused.pauseOnDOMEvents", - XHR: "whyPaused.xhr", - promiseRejection: "whyPaused.promiseRejection", - assert: "whyPaused.assert", - debugCommand: "whyPaused.debugCommand", - other: "whyPaused.other" -}; /* 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 getPauseReason(why) { - if (!why) { - return null; - } - - const reasonType = why.type; - if (!reasons[reasonType]) { - console.log("Please file an issue: reasonType=", reasonType); - } - return reasons[reasonType]; -} - -function isException(why) { - return why && why.type && why.type === "exception"; -} - -function isInterrupted(why) { - return why && why.type && why.type === "interrupted"; -} - -function inDebuggerEval(why) { - if (why && why.type === "exception" && why.exception && why.exception.preview && why.exception.preview.fileName) { - return why.exception.preview.fileName === "debugger eval code"; - } - - return false; -} - -/***/ }), +/* 1400 */, /* 1401 */ /***/ (function(module, exports, __webpack_require__) { @@ -35210,30 +35188,33 @@ class SearchBar extends _react.Component { }; this.closeSearch = e => { - const { editor, searchOn } = this.props; - + const { closeFileSearch, editor, searchOn } = this.props; if (editor && searchOn) { this.clearSearch(); - this.props.closeFileSearch(editor); + closeFileSearch(editor); e.stopPropagation(); e.preventDefault(); } + this.setState({ query: "", inputFocused: false }); }; this.toggleSearch = e => { e.stopPropagation(); e.preventDefault(); - const { editor } = this.props; + const { editor, searchOn, setActiveSearch } = this.props; - if (!this.props.searchOn) { - this.props.setActiveSearch("file"); + if (!searchOn) { + setActiveSearch("file"); } - if (this.props.searchOn && editor) { - const selection = editor.codeMirror.getSelection(); - this.setState({ query: selection }); - if (selection !== "") { - this.doSearch(selection); + if (searchOn && editor) { + const query = editor.codeMirror.getSelection() || this.state.query; + + if (query !== "") { + this.setState({ query, inputFocused: true }); + this.doSearch(query); + } else { + this.setState({ query: "", inputFocused: true }); } } }; @@ -35274,6 +35255,10 @@ class SearchBar extends _react.Component { return this.doSearch(e.target.value); }; + this.onBlur = e => { + this.setState({ inputFocused: false }); + }; + this.onKeyDown = e => { if (e.key !== "Enter" && e.key !== "F3") { return; @@ -35338,7 +35323,8 @@ class SearchBar extends _react.Component { query: props.query, selectedResultIndex: 0, count: 0, - index: -1 + index: -1, + inputFocused: false }; } @@ -35422,11 +35408,13 @@ class SearchBar extends _react.Component { placeholder: L10N.getStr("sourceSearch.search.placeholder"), summaryMsg: this.buildSummaryMsg(), onChange: this.onChange, + onBlur: this.onBlur, showErrorEmoji: this.shouldShowErrorEmoji(), onKeyDown: this.onKeyDown, handleNext: e => this.traverseResults(e, false), handlePrev: e => this.traverseResults(e, true), - handleClose: this.closeSearch + handleClose: this.closeSearch, + shouldFocus: this.state.inputFocused }), _react2.default.createElement( "div", @@ -39255,7 +39243,7 @@ var _editor = __webpack_require__(1358); var _source = __webpack_require__(1356); -var _pause = __webpack_require__(1400); +var _pause = __webpack_require__(2419); var _indentation = __webpack_require__(1438); @@ -39727,7 +39715,7 @@ function getMenuItems(event, { id: "node-menu-show-source", label: revealInTreeLabel, accesskey: revealInTreeKey, - disabled: isPrettyPrinted, + disabled: false, click: () => showSource(sourceId) }; @@ -40454,7 +40442,7 @@ var _source = __webpack_require__(1356); var _selectors = __webpack_require__(1352); -var _pause = __webpack_require__(1400); +var _pause = __webpack_require__(2419); var _breakpoint = __webpack_require__(1364); @@ -41221,7 +41209,7 @@ var _react = __webpack_require__(0); var _react2 = _interopRequireDefault(_react); -var _pause = __webpack_require__(1400); +var _pause = __webpack_require__(2419); __webpack_require__(1337); @@ -43363,7 +43351,9 @@ function hasAwait(source, pauseLocation) { return false; } - return source.text.split("\n")[line - 1].slice(column, column + 200).match(/(yield|await)/); + const snippet = source.text.split("\n")[line - 1].slice(column - 50, column + 50); + + return !!snippet.match(/(yield|await)/); } /** @@ -43382,6 +43372,7 @@ function astCommand(stepType) { // This type definition is ambiguous: const frame = (0, _selectors.getTopFrame)(getState()); const source = (0, _selectors.getSelectedSource)(getState()).toJS(); + if (source && hasAwait(source, frame.location)) { const nextLocation = await (0, _parser.getNextStep)(source.id, frame.location); if (nextLocation) { @@ -43593,8 +43584,17 @@ var _sources = __webpack_require__(1797); var _ui = __webpack_require__(1385); +var _commands = __webpack_require__(1637); + +var _pause = __webpack_require__(2419); + +var _mapFrames = __webpack_require__(1804); + var _fetchScopes = __webpack_require__(1655); +async function getOriginalSourceForFrame(state, frame) { + return (0, _selectors.getSources)(state).get(frame.location.sourceId); +} /** * Debugger has just paused * @@ -43609,12 +43609,26 @@ var _fetchScopes = __webpack_require__(1655); function paused(pauseInfo) { return async function ({ dispatch, getState, client, sourceMaps }) { const { frames, why, loadedObjects } = pauseInfo; + const rootFrame = frames.length > 0 ? frames[0] : null; + + if (rootFrame) { + const mappedFrame = await (0, _mapFrames.updateFrameLocation)(rootFrame, sourceMaps); + const source = await getOriginalSourceForFrame(getState(), mappedFrame); + + // Ensure that the original file has loaded if there is one. + await dispatch((0, _sources.loadSourceText)(source)); + + if (await (0, _pause.shouldStep)(mappedFrame, getState(), sourceMaps)) { + dispatch((0, _commands.command)("stepOver")); + return; + } + } dispatch({ type: "PAUSED", why, frames, - selectedFrameId: frames[0] ? frames[0].id : undefined, + selectedFrameId: rootFrame ? rootFrame.id : undefined, loadedObjects: loadedObjects || [] }); @@ -43657,7 +43671,7 @@ var _selectors = __webpack_require__(1352); var _expressions = __webpack_require__(1398); -var _pause = __webpack_require__(1400); +var _pause = __webpack_require__(2419); /** * Debugger has just resumed @@ -44001,7 +44015,7 @@ class QuickOpenModal extends _react.Component { return; } - if (query == "") { + if (query == "" && !this.isShortcutQuery()) { return this.showTopSources(); } @@ -48484,6 +48498,7 @@ var _extends = Object.assign || function (target) { for (var i = 1; i < argument * @module actions/sources */ +exports.loadSourceMap = loadSourceMap; exports.newSource = newSource; exports.newSources = newSources; @@ -48510,25 +48525,25 @@ function createOriginalSource(originalUrl, generatedSource, sourceMaps) { }; } -// TODO: It would be nice to make getOriginalURLs a safer api -async function loadOriginalSourceUrls(sourceMaps, generatedSource) { - try { - return await sourceMaps.getOriginalURLs(generatedSource); - } catch (e) { - console.error(e); - return null; - } -} - /** * @memberof actions/sources * @static */ function loadSourceMap(generatedSource) { return async function ({ dispatch, getState, sourceMaps }) { - const urls = await loadOriginalSourceUrls(sourceMaps, generatedSource); + let urls; + try { + urls = await sourceMaps.getOriginalURLs(generatedSource); + } catch (e) { + console.error(e); + urls = null; + } if (!urls) { - // If this source doesn't have a sourcemap, do nothing. + // If this source doesn't have a sourcemap, enable it for pretty printing + dispatch({ + type: "UPDATE_SOURCE", + source: _extends({}, generatedSource, { sourceMapURL: "" }) + }); return; } @@ -48836,6 +48851,7 @@ Object.defineProperty(exports, "__esModule", { var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; +exports.updateFrameLocation = updateFrameLocation; exports.mapFrames = mapFrames; var _selectors = __webpack_require__(1352); @@ -50361,6 +50377,7 @@ class ProjectSearch extends _react.Component { if (this.isProjectSearchEnabled()) { return closeProjectSearch(); } + return setActiveSearch("project"); }; @@ -50880,11 +50897,11 @@ class Tab extends _react.PureComponent { }) }]; - if (!isPrettySource) { - items.push({ - item: _extends({}, tabMenuItems.showSource, { click: () => showSource(tab) }) - }); + items.push({ + item: _extends({}, tabMenuItems.showSource, { click: () => showSource(tab) }) + }); + if (!isPrettySource) { items.push({ item: _extends({}, tabMenuItems.prettyPrint, { click: () => togglePrettyPrint(tab) @@ -52110,6 +52127,213 @@ function getExpressionFromCoords(cm, coord) { return { expression, location }; } +/***/ }), +/* 2361 */, +/* 2362 */, +/* 2363 */, +/* 2364 */, +/* 2365 */, +/* 2366 */, +/* 2367 */, +/* 2368 */, +/* 2369 */, +/* 2370 */, +/* 2371 */, +/* 2372 */, +/* 2373 */, +/* 2374 */, +/* 2375 */, +/* 2376 */, +/* 2377 */, +/* 2378 */, +/* 2379 */, +/* 2380 */, +/* 2381 */, +/* 2382 */, +/* 2383 */, +/* 2384 */, +/* 2385 */, +/* 2386 */, +/* 2387 */, +/* 2388 */, +/* 2389 */, +/* 2390 */, +/* 2391 */, +/* 2392 */, +/* 2393 */, +/* 2394 */, +/* 2395 */, +/* 2396 */, +/* 2397 */, +/* 2398 */, +/* 2399 */, +/* 2400 */, +/* 2401 */, +/* 2402 */, +/* 2403 */, +/* 2404 */, +/* 2405 */, +/* 2406 */, +/* 2407 */, +/* 2408 */, +/* 2409 */, +/* 2410 */, +/* 2411 */, +/* 2412 */, +/* 2413 */, +/* 2414 */, +/* 2415 */, +/* 2416 */, +/* 2417 */, +/* 2418 */, +/* 2419 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _why = __webpack_require__(2420); + +Object.keys(_why).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _why[key]; + } + }); +}); + +var _stepping = __webpack_require__(2421); + +Object.keys(_stepping).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _stepping[key]; + } + }); +}); + +/***/ }), +/* 2420 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getPauseReason = getPauseReason; +exports.isException = isException; +exports.isInterrupted = isInterrupted; +exports.inDebuggerEval = inDebuggerEval; + + +// Map protocol pause "why" reason to a valid L10N key +// These are the known unhandled reasons: +// "breakpointConditionThrown", "clientEvaluated" +// "interrupted", "attached" +const reasons = { + debuggerStatement: "whyPaused.debuggerStatement", + breakpoint: "whyPaused.breakpoint", + exception: "whyPaused.exception", + resumeLimit: "whyPaused.resumeLimit", + pauseOnDOMEvents: "whyPaused.pauseOnDOMEvents", + breakpointConditionThrown: "whyPaused.breakpointConditionThrown", + + // V8 + DOM: "whyPaused.breakpoint", + EventListener: "whyPaused.pauseOnDOMEvents", + XHR: "whyPaused.xhr", + promiseRejection: "whyPaused.promiseRejection", + assert: "whyPaused.assert", + debugCommand: "whyPaused.debugCommand", + other: "whyPaused.other" +}; /* 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 getPauseReason(why) { + if (!why) { + return null; + } + + const reasonType = why.type; + if (!reasons[reasonType]) { + console.log("Please file an issue: reasonType=", reasonType); + } + return reasons[reasonType]; +} + +function isException(why) { + return why && why.type && why.type === "exception"; +} + +function isInterrupted(why) { + return why && why.type && why.type === "interrupted"; +} + +function inDebuggerEval(why) { + if (why && why.type === "exception" && why.exception && why.exception.preview && why.exception.preview.fileName) { + return why.exception.preview.fileName === "debugger eval code"; + } + + return false; +} + +/***/ }), +/* 2421 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.shouldStep = shouldStep; + +var _lodash = __webpack_require__(2); + +var _devtoolsSourceMap = __webpack_require__(1360); + +var _selectors = __webpack_require__(1352); + +var _parser = __webpack_require__(1365); + +/* 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 . */ + +async function shouldStep(rootFrame, state, sourceMaps) { + if (!rootFrame) { + return false; + } + + const selectedSource = (0, _selectors.getSelectedSource)(state); + const previousFrameInfo = (0, _selectors.getPreviousPauseFrameLocation)(state); + + let previousFrameLoc; + let currentFrameLoc; + + if (selectedSource && (0, _devtoolsSourceMap.isOriginalId)(selectedSource.get("id"))) { + currentFrameLoc = rootFrame.location; + previousFrameLoc = previousFrameInfo && previousFrameInfo.location; + } else { + currentFrameLoc = rootFrame.generatedLocation; + previousFrameLoc = previousFrameInfo && previousFrameInfo.generatedLocation; + } + + return (0, _devtoolsSourceMap.isOriginalId)(currentFrameLoc.sourceId) && (previousFrameLoc && (0, _lodash.isEqual)(previousFrameLoc, currentFrameLoc) || (await (0, _parser.isInvalidPauseLocation)(currentFrameLoc))); +} + /***/ }) /******/ ]); }); \ No newline at end of file diff --git a/devtools/client/debugger/new/parser-worker.js b/devtools/client/debugger/new/parser-worker.js index 2b2faeab3d8c..e90ee9f6d2a8 100644 --- a/devtools/client/debugger/new/parser-worker.js +++ b/devtools/client/debugger/new/parser-worker.js @@ -34597,15 +34597,15 @@ var _validate = __webpack_require__(1629); var _frameworks = __webpack_require__(1703); +var _pauseLocation = __webpack_require__(2422); + var _devtoolsUtils = __webpack_require__(1363); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -/* 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 . */ - -const { workerHandler } = _devtoolsUtils.workerUtils; +const { workerHandler } = _devtoolsUtils.workerUtils; /* 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 . */ self.onmessage = workerHandler({ getClosestExpression: _closest.getClosestExpression, @@ -34618,6 +34618,7 @@ self.onmessage = workerHandler({ hasSource: _sources.hasSource, setSource: _sources.setSource, clearSources: _sources.clearSources, + isInvalidPauseLocation: _pauseLocation.isInvalidPauseLocation, getVariablesInScope: _scopes.getVariablesInScope, getNextStep: _steps.getNextStep, getEmptyLines: _getEmptyLines2.default, @@ -60818,6 +60819,82 @@ function stripModuleScope(rootScope) { }); } +/***/ }), +/* 2415 */, +/* 2416 */, +/* 2417 */, +/* 2418 */, +/* 2419 */, +/* 2420 */, +/* 2421 */, +/* 2422 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.isInvalidPauseLocation = isInvalidPauseLocation; + +var _types = __webpack_require__(2268); + +var t = _interopRequireWildcard(_types); + +var _ast = __webpack_require__(1375); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +/* 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 . */ + +const STOP = {}; + +function isInvalidPauseLocation(location) { + const state = { + invalid: false, + location + }; + + try { + (0, _ast.fastTraverseAst)(location.sourceId, { enter: invalidLocationVisitor }, state); + } catch (e) { + if (e !== STOP) { + throw e; + } + } + + return state.invalid; +} + +function invalidLocationVisitor(node, ancestors, state) { + const { location } = state; + + if (node.loc.end.line < location.line) { + return; + } + if (node.loc.start.line > location.line) { + throw STOP; + } + + if (location.line === node.loc.start.line && location.column >= node.loc.start.column && t.isFunction(node) && !t.isArrowFunctionExpression(node) && (location.line < node.body.loc.start.line || location.line === node.body.loc.start.line && location.column <= node.body.loc.start.column)) { + // Disallow pausing _inside_ in function arguments to avoid pausing inside + // of destructuring and other logic. + state.invalid = true; + throw STOP; + } + + if (location.line === node.loc.start.line && location.column === node.loc.start.column && t.isBlockStatement(node)) { + // Disallow pausing directly before the opening curly of a block statement. + // Babel occasionally maps statements with unknown original positions to + // this location. + state.invalid = true; + throw STOP; + } +} + /***/ }) /******/ ]); }); \ 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 381d9b9395a9..04245303e25e 100644 --- a/devtools/client/debugger/new/test/mochitest/browser.ini +++ b/devtools/client/debugger/new/test/mochitest/browser.ini @@ -117,8 +117,9 @@ support-files = [browser_dbg-asm.js] [browser_dbg-async-stepping.js] -[browser_dbg-babel.js] +[browser_dbg-babel-scopes.js] skip-if = (os == "win" && ccov) # bug 1438797 +[browser_dbg-babel-stepping.js] [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-babel.js b/devtools/client/debugger/new/test/mochitest/browser_dbg-babel-scopes.js similarity index 100% rename from devtools/client/debugger/new/test/mochitest/browser_dbg-babel.js rename to devtools/client/debugger/new/test/mochitest/browser_dbg-babel-scopes.js diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg-babel-stepping.js b/devtools/client/debugger/new/test/mochitest/browser_dbg-babel-stepping.js new file mode 100644 index 000000000000..2c29d45536d6 --- /dev/null +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-babel-stepping.js @@ -0,0 +1,154 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests for stepping through Babel's compile output. + +async function breakpointSteps(dbg, fixture, { line, column }, steps) { + const { selectors: { getBreakpoint, getBreakpoints }, getState } = dbg; + + const filename = `fixtures/${fixture}/input.js`; + await waitForSources(dbg, filename); + + ok(true, "Original sources exist"); + const source = findSource(dbg, filename); + + await selectSource(dbg, source); + + // Test that breakpoint is not off by a line. + await addBreakpoint(dbg, source, line); + + is(getBreakpoints(getState()).size, 1, "One breakpoint exists"); + ok( + getBreakpoint(getState(), { sourceId: source.id, line, column }), + "Breakpoint has correct line" + ); + + const fnName = fixture.replace(/-([a-z])/g, (s, c) => c.toUpperCase()); + + const invokeResult = invokeInTab(fnName); + + let invokeFailed = await Promise.race([ + waitForPaused(dbg), + invokeResult.then(() => new Promise(() => {}), () => true) + ]); + + if (invokeFailed) { + return invokeResult; + } + + assertPausedLocation(dbg); + + await removeBreakpoint(dbg, source.id, line, column); + + is(getBreakpoints(getState()).size, 0, "Breakpoint reverted"); + + await runSteps(dbg, source, steps); + + await resume(dbg); + + // If the invoke errored later somehow, capture here so the error is + // reported nicely. + await invokeResult; + + ok(true, `Ran tests for ${fixture} at line ${line} column ${column}`); +} + +async function runSteps(dbg, source, steps) { + const { selectors: { getVisibleSelectedFrame }, getState } = dbg; + + for (const [i, [type, position]] of steps.entries()) { + switch (type) { + case "stepOver": + await stepOver(dbg); + break; + case "stepIn": + await stepIn(dbg); + break; + default: + throw new Error("Unknown stepping type"); + } + + const { location } = getVisibleSelectedFrame(getState()); + + is(location.sourceId, source.id, `Step ${i} has correct sourceId`); + is(location.line, position.line, `Step ${i} has correct line`); + is(location.column, position.column, `Step ${i} has correct column`); + + assertPausedLocation(dbg); + } +} + +add_task(async function() { + requestLongerTimeout(4); + + const dbg = await initDebugger("doc-babel.html"); + + await breakpointSteps(dbg, "step-over-for-of", { line: 4, column: 2 }, [ + ["stepOver", { line: 6, column: 2 }], + ["stepOver", { line: 7, column: 4 }], + ["stepOver", { line: 6, column: 2 }], + ["stepOver", { line: 7, column: 4 }], + ["stepOver", { line: 6, column: 2 }], + ["stepOver", { line: 10, column: 2 }] + ]); + + // This codifies the current behavior, but stepping twice over the for + // header isn't ideal. + await breakpointSteps(dbg, "step-over-for-of-array", { line: 3, column: 2 }, [ + ["stepOver", { line: 5, column: 2 }], + ["stepOver", { line: 5, column: 7 }], + ["stepOver", { line: 6, column: 4 }], + ["stepOver", { line: 5, column: 2 }], + ["stepOver", { line: 5, column: 7 }], + ["stepOver", { line: 6, column: 4 }], + ["stepOver", { line: 5, column: 2 }], + ["stepOver", { line: 9, column: 2 }] + ]); + + // The closure means it isn't actually possible to step into the for body, + // and Babel doesn't map the _loop() call, so we step past it automatically. + await breakpointSteps( + dbg, + "step-over-for-of-closure", + { line: 6, column: 2 }, + [ + ["stepOver", { line: 8, column: 2 }], + ["stepOver", { line: 12, column: 2 }] + ] + ); + + // Same as the previous, not possible to step into the body. The less + // complicated array logic makes it possible to step into the header at least, + // but this does end up double-visiting the for head. + await breakpointSteps( + dbg, + "step-over-for-of-array-closure", + { line: 3, column: 2 }, + [ + ["stepOver", { line: 5, column: 2 }], + ["stepOver", { line: 5, column: 7 }], + ["stepOver", { line: 5, column: 2 }], + ["stepOver", { line: 5, column: 7 }], + ["stepOver", { line: 5, column: 2 }], + ["stepOver", { line: 9, column: 2 }] + ] + ); + + await breakpointSteps( + dbg, + "step-over-function-params", + { line: 6, column: 2 }, + [["stepOver", { line: 7, column: 2 }], ["stepIn", { line: 2, column: 2 }]] + ); + + await breakpointSteps( + dbg, + "step-over-regenerator-await", + { line: 2, column: 2 }, + [ + // Won't work until a fix to regenerator lands and we rebuild. + // https://github.com/facebook/regenerator/issues/342 + // ["stepOver", { line: 4, column: 2 }], + ] + ); +}); diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg-breaking-from-console.js b/devtools/client/debugger/new/test/mochitest/browser_dbg-breaking-from-console.js index 883a297c19b5..dd41c9c6957f 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg-breaking-from-console.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-breaking-from-console.js @@ -26,7 +26,7 @@ add_task(async function() { const { selectors: { getSelectedSource }, getState } = dbg; // Make sure the thread is paused in the right source and location - await waitForDispatch(dbg, "LOAD_SOURCE_TEXT"); + await waitForPaused(dbg); is(dbg.win.cm.getValue(), "debugger"); const source = getSelectedSource(getState()).toJS(); assertPausedLocation(dbg); diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps-reload.js b/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps-reload.js index 9899ffd47468..c879192c05d2 100644 --- a/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps-reload.js +++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps-reload.js @@ -53,7 +53,6 @@ add_task(async function() { await reload(dbg); await waitForSource(dbg, "v1"); await syncBp; - await waitForSelectedSource(dbg, "v1"); is(getBreakpoints(dbg).length, 0, "No breakpoints"); }); diff --git a/devtools/client/sourceeditor/README b/devtools/client/sourceeditor/README index a49eb315fa97..85fe6417c4ba 100644 --- a/devtools/client/sourceeditor/README +++ b/devtools/client/sourceeditor/README @@ -5,7 +5,7 @@ code, and optionally help with indentation. # Upgrade -Currently used version is 5.34.0. To upgrade: download a new version of +Currently used version is 5.35.0. To upgrade: download a new version of CodeMirror from the project's page [1] and replace all JavaScript and CSS files inside the codemirror directory [2]. diff --git a/devtools/client/sourceeditor/codemirror/addon/search/match-highlighter.js b/devtools/client/sourceeditor/codemirror/addon/search/match-highlighter.js index 73ba0e053709..35221711790e 100644 --- a/devtools/client/sourceeditor/codemirror/addon/search/match-highlighter.js +++ b/devtools/client/sourceeditor/codemirror/addon/search/match-highlighter.js @@ -90,7 +90,7 @@ var state = cm.state.matchHighlighter; cm.addOverlay(state.overlay = makeOverlay(query, hasBoundary, style)); if (state.options.annotateScrollbar && cm.showMatchesOnScrollbar) { - var searchFor = hasBoundary ? new RegExp("\\b" + query + "\\b") : query; + var searchFor = hasBoundary ? new RegExp("\\b" + query.replace(/[\\\[+*?(){|^$]/g, "\\$&") + "\\b") : query; state.matchesonscroll = cm.showMatchesOnScrollbar(searchFor, false, {className: "CodeMirror-selection-highlight-scrollbar"}); } diff --git a/devtools/client/sourceeditor/codemirror/addon/search/searchcursor.js b/devtools/client/sourceeditor/codemirror/addon/search/searchcursor.js index 58bc47c2c3e6..e606c5e7cf59 100644 --- a/devtools/client/sourceeditor/codemirror/addon/search/searchcursor.js +++ b/devtools/client/sourceeditor/codemirror/addon/search/searchcursor.js @@ -19,8 +19,11 @@ + (regexp.multiline ? "m" : "") } - function ensureGlobal(regexp) { - return regexp.global ? regexp : new RegExp(regexp.source, regexpFlags(regexp) + "g") + function ensureFlags(regexp, flags) { + var current = regexpFlags(regexp), target = current + for (var i = 0; i < flags.length; i++) if (target.indexOf(flags.charAt(i)) == -1) + target += flags.charAt(i) + return current == target ? regexp : new RegExp(regexp.source, target) } function maybeMultiline(regexp) { @@ -28,7 +31,7 @@ } function searchRegexpForward(doc, regexp, start) { - regexp = ensureGlobal(regexp) + regexp = ensureFlags(regexp, "g") for (var line = start.line, ch = start.ch, last = doc.lastLine(); line <= last; line++, ch = 0) { regexp.lastIndex = ch var string = doc.getLine(line), match = regexp.exec(string) @@ -42,7 +45,7 @@ function searchRegexpForwardMultiline(doc, regexp, start) { if (!maybeMultiline(regexp)) return searchRegexpForward(doc, regexp, start) - regexp = ensureGlobal(regexp) + regexp = ensureFlags(regexp, "gm") var string, chunk = 1 for (var line = start.line, last = doc.lastLine(); line <= last;) { // This grows the search buffer in exponentially-sized chunks @@ -51,6 +54,7 @@ // searching for something that has tons of matches), but at the // same time, the amount of retries is limited. for (var i = 0; i < chunk; i++) { + if (line > last) break var curLine = doc.getLine(line++) string = string == null ? curLine : string + "\n" + curLine } @@ -81,7 +85,7 @@ } function searchRegexpBackward(doc, regexp, start) { - regexp = ensureGlobal(regexp) + regexp = ensureFlags(regexp, "g") for (var line = start.line, ch = start.ch, first = doc.firstLine(); line >= first; line--, ch = -1) { var string = doc.getLine(line) if (ch > -1) string = string.slice(0, ch) @@ -94,7 +98,7 @@ } function searchRegexpBackwardMultiline(doc, regexp, start) { - regexp = ensureGlobal(regexp) + regexp = ensureFlags(regexp, "gm") var string, chunk = 1 for (var line = start.line, first = doc.firstLine(); line >= first;) { for (var i = 0; i < chunk; i++) { @@ -213,7 +217,7 @@ return (reverse ? searchStringBackward : searchStringForward)(doc, query, pos, caseFold) } } else { - query = ensureGlobal(query) + query = ensureFlags(query, "gm") if (!options || options.multiline !== false) this.matches = function(reverse, pos) { return (reverse ? searchRegexpBackwardMultiline : searchRegexpForwardMultiline)(doc, query, pos) diff --git a/devtools/client/sourceeditor/codemirror/codemirror.bundle.js b/devtools/client/sourceeditor/codemirror/codemirror.bundle.js index 0f81ea6dd2ce..83229e726b75 100644 --- a/devtools/client/sourceeditor/codemirror/codemirror.bundle.js +++ b/devtools/client/sourceeditor/codemirror/codemirror.bundle.js @@ -5454,7 +5454,8 @@ var CodeMirror = // Revert a change stored in a document's history. function makeChangeFromHistory(doc, type, allowSelectionOnly) { - if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) { return } + var suppress = doc.cm && doc.cm.state.suppressEdits + if (suppress && !allowSelectionOnly) { return } var hist = doc.history, event, selAfter = doc.sel var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done @@ -5479,8 +5480,10 @@ var CodeMirror = return } selAfter = event - } - else { break } + } else if (suppress) { + source.push(event) + return + } else { break } } // Build up a reverse change object to add to the opposite history @@ -5956,7 +5959,7 @@ var CodeMirror = } return true }) - signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)) + if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)) } return widget } @@ -9892,7 +9895,7 @@ var CodeMirror = addLegacyProps(CodeMirror) - CodeMirror.version = "5.34.0" + CodeMirror.version = "5.35.0" return CodeMirror; @@ -9923,8 +9926,11 @@ var CodeMirror = + (regexp.multiline ? "m" : "") } - function ensureGlobal(regexp) { - return regexp.global ? regexp : new RegExp(regexp.source, regexpFlags(regexp) + "g") + function ensureFlags(regexp, flags) { + var current = regexpFlags(regexp), target = current + for (var i = 0; i < flags.length; i++) if (target.indexOf(flags.charAt(i)) == -1) + target += flags.charAt(i) + return current == target ? regexp : new RegExp(regexp.source, target) } function maybeMultiline(regexp) { @@ -9932,7 +9938,7 @@ var CodeMirror = } function searchRegexpForward(doc, regexp, start) { - regexp = ensureGlobal(regexp) + regexp = ensureFlags(regexp, "g") for (var line = start.line, ch = start.ch, last = doc.lastLine(); line <= last; line++, ch = 0) { regexp.lastIndex = ch var string = doc.getLine(line), match = regexp.exec(string) @@ -9946,7 +9952,7 @@ var CodeMirror = function searchRegexpForwardMultiline(doc, regexp, start) { if (!maybeMultiline(regexp)) return searchRegexpForward(doc, regexp, start) - regexp = ensureGlobal(regexp) + regexp = ensureFlags(regexp, "gm") var string, chunk = 1 for (var line = start.line, last = doc.lastLine(); line <= last;) { // This grows the search buffer in exponentially-sized chunks @@ -9955,6 +9961,7 @@ var CodeMirror = // searching for something that has tons of matches), but at the // same time, the amount of retries is limited. for (var i = 0; i < chunk; i++) { + if (line > last) break var curLine = doc.getLine(line++) string = string == null ? curLine : string + "\n" + curLine } @@ -9985,7 +9992,7 @@ var CodeMirror = } function searchRegexpBackward(doc, regexp, start) { - regexp = ensureGlobal(regexp) + regexp = ensureFlags(regexp, "g") for (var line = start.line, ch = start.ch, first = doc.firstLine(); line >= first; line--, ch = -1) { var string = doc.getLine(line) if (ch > -1) string = string.slice(0, ch) @@ -9998,7 +10005,7 @@ var CodeMirror = } function searchRegexpBackwardMultiline(doc, regexp, start) { - regexp = ensureGlobal(regexp) + regexp = ensureFlags(regexp, "gm") var string, chunk = 1 for (var line = start.line, first = doc.firstLine(); line >= first;) { for (var i = 0; i < chunk; i++) { @@ -10117,7 +10124,7 @@ var CodeMirror = return (reverse ? searchStringBackward : searchStringForward)(doc, query, pos, caseFold) } } else { - query = ensureGlobal(query) + query = ensureFlags(query, "gm") if (!options || options.multiline !== false) this.matches = function(reverse, pos) { return (reverse ? searchRegexpBackwardMultiline : searchRegexpForwardMultiline)(doc, query, pos) @@ -11438,6 +11445,7 @@ var CodeMirror = if (type == "{") return contCommasep(objprop, "}", null, maybeop); if (type == "quasi") return pass(quasi, maybeop); if (type == "new") return cont(maybeTarget(noComma)); + if (type == "import") return cont(expression); return cont(); } function maybeexpression(type) { @@ -11632,7 +11640,7 @@ var CodeMirror = } function afterType(type, value) { if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) - if (value == "|" || type == ".") return cont(typeexpr) + if (value == "|" || type == "." || value == "&") return cont(typeexpr) if (type == "[") return cont(expect("]"), afterType) if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) } } @@ -11675,7 +11683,8 @@ var CodeMirror = function maybeelse(type, value) { if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex); } - function forspec(type) { + function forspec(type, value) { + if (value == "await") return cont(forspec); if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex); } function forspec1(type) { @@ -11764,6 +11773,7 @@ var CodeMirror = } function afterImport(type) { if (type == "string") return cont(); + if (type == "(") return pass(expression); return pass(importSpec, maybeMoreImports, maybeFrom); } function importSpec(type, value) { @@ -14416,7 +14426,7 @@ var CodeMirror = blockKeywords: words("case do else for if switch while struct"), defKeywords: words("struct"), typeFirstDefinitions: true, - atoms: words("null true false"), + atoms: words("NULL true false"), hooks: {"#": cppHook, "*": pointerHook}, modeProps: {fold: ["brace", "include"]} }); @@ -14432,7 +14442,7 @@ var CodeMirror = blockKeywords: words("catch class do else finally for if struct switch try while"), defKeywords: words("class namespace struct enum union"), typeFirstDefinitions: true, - atoms: words("true false null"), + atoms: words("true false NULL"), dontIndentStatements: /^template$/, isIdentifierChar: /[\w\$_~\xa1-\uffff]/, hooks: { @@ -14639,22 +14649,24 @@ var CodeMirror = name: "clike", keywords: words( /*keywords*/ - "package as typealias class interface this super val " + - "var fun for is in This throw return " + + "package as typealias class interface this super val operator " + + "var fun for is in This throw return annotation " + "break continue object if else while do try when !in !is as? " + /*soft keywords*/ "file import where by get set abstract enum open inner override private public internal " + "protected catch finally out final vararg reified dynamic companion constructor init " + "sealed field property receiver param sparam lateinit data inline noinline tailrec " + - "external annotation crossinline const operator infix suspend actual expect" + "external annotation crossinline const operator infix suspend actual expect setparam" ), types: words( /* package java.lang */ "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " + "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " + "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " + - "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void" + "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void Annotation Any BooleanArray " + + "ByteArray Char CharArray DeprecationLevel DoubleArray Enum FloatArray Function Int IntArray Lazy " + + "LazyThreadSafetyMode LongArray Nothing ShortArray Unit" ), intendSwitch: false, indentStatements: false, @@ -16448,7 +16460,7 @@ var CodeMirror = } function handleKeyNonInsertMode() { - if (handleMacroRecording() || handleEsc()) { return true; }; + if (handleMacroRecording() || handleEsc()) { return true; } var keys = vim.inputState.keyBuffer = vim.inputState.keyBuffer + key; if (/^[1-9]\d*$/.test(keys)) { return true; } @@ -17033,7 +17045,7 @@ var CodeMirror = } else { if (vim.visualMode) { showPrompt(cm, { onClose: onPromptClose, prefix: ':', value: '\'<,\'>', - onKeyDown: onPromptKeyDown}); + onKeyDown: onPromptKeyDown, selectValueOnOpen: false}); } else { showPrompt(cm, { onClose: onPromptClose, prefix: ':', onKeyDown: onPromptKeyDown}); @@ -19290,7 +19302,15 @@ var CodeMirror = } } function splitBySlash(argString) { - var slashes = findUnescapedSlashes(argString) || []; + return splitBySeparator(argString, '/'); + } + + function findUnescapedSlashes(argString) { + return findUnescapedSeparators(argString, '/'); + } + + function splitBySeparator(argString, separator) { + var slashes = findUnescapedSeparators(argString, separator) || []; if (!slashes.length) return []; var tokens = []; // in case of strings like foo/bar @@ -19302,12 +19322,15 @@ var CodeMirror = return tokens; } - function findUnescapedSlashes(str) { + function findUnescapedSeparators(str, separator) { + if (!separator) + separator = '/'; + var escapeNextChar = false; var slashes = []; for (var i = 0; i < str.length; i++) { var c = str.charAt(i); - if (!escapeNextChar && c == '/') { + if (!escapeNextChar && c == separator) { slashes.push(i); } escapeNextChar = !escapeNextChar && (c == '\\'); @@ -20173,7 +20196,7 @@ var CodeMirror = 'any other getSearchCursor implementation.'); } var argString = params.argString; - var tokens = argString ? splitBySlash(argString) : []; + var tokens = argString ? splitBySeparator(argString, argString[0]) : []; var regexPart, replacePart = '', trailing, flagsPart, count; var confirm = false; // Whether to confirm each replace. var global = false; // True to replace all instances on a line, false to replace only 1. @@ -20217,7 +20240,7 @@ var CodeMirror = global = true; flagsPart.replace('g', ''); } - regexPart = regexPart + '/' + flagsPart; + regexPart = regexPart.replace(/\//g, "\\/") + '/' + flagsPart; } } if (regexPart) { @@ -20433,7 +20456,7 @@ var CodeMirror = } if (!confirm) { replaceAll(); - if (callback) { callback(); }; + if (callback) { callback(); } return; } showPrompt(cm, { @@ -20569,7 +20592,7 @@ var CodeMirror = exitInsertMode(cm); } } - }; + } macroModeState.isPlaying = false; } @@ -20773,7 +20796,7 @@ var CodeMirror = exitInsertMode(cm); } macroModeState.isPlaying = false; - }; + } function repeatInsertModeChanges(cm, changes, repeat) { function keyHandler(binding) { @@ -20988,8 +21011,14 @@ var CodeMirror = var ranges = cm.listSelections(), newRanges = []; for (var i = 0; i < ranges.length; i++) { var range = ranges[i]; - var newAnchor = cm.findPosV(range.anchor, dir, "line"); - var newHead = cm.findPosV(range.head, dir, "line"); + var newAnchor = cm.findPosV( + range.anchor, dir, "line", range.anchor.goalColumn); + var newHead = cm.findPosV( + range.head, dir, "line", range.head.goalColumn); + newAnchor.goalColumn = range.anchor.goalColumn != null ? + range.anchor.goalColumn : cm.cursorCoords(range.anchor, "div").left; + newHead.goalColumn = range.head.goalColumn != null ? + range.head.goalColumn : cm.cursorCoords(range.head, "div").left; var newRange = {anchor: newAnchor, head: newHead}; newRanges.push(range); newRanges.push(newRange); diff --git a/devtools/client/sourceeditor/codemirror/keymap/sublime.js b/devtools/client/sourceeditor/codemirror/keymap/sublime.js index 7a9aadd33097..aa9b65e3b16d 100644 --- a/devtools/client/sourceeditor/codemirror/keymap/sublime.js +++ b/devtools/client/sourceeditor/codemirror/keymap/sublime.js @@ -156,8 +156,14 @@ var ranges = cm.listSelections(), newRanges = []; for (var i = 0; i < ranges.length; i++) { var range = ranges[i]; - var newAnchor = cm.findPosV(range.anchor, dir, "line"); - var newHead = cm.findPosV(range.head, dir, "line"); + var newAnchor = cm.findPosV( + range.anchor, dir, "line", range.anchor.goalColumn); + var newHead = cm.findPosV( + range.head, dir, "line", range.head.goalColumn); + newAnchor.goalColumn = range.anchor.goalColumn != null ? + range.anchor.goalColumn : cm.cursorCoords(range.anchor, "div").left; + newHead.goalColumn = range.head.goalColumn != null ? + range.head.goalColumn : cm.cursorCoords(range.head, "div").left; var newRange = {anchor: newAnchor, head: newHead}; newRanges.push(range); newRanges.push(newRange); diff --git a/devtools/client/sourceeditor/codemirror/keymap/vim.js b/devtools/client/sourceeditor/codemirror/keymap/vim.js index b08226818372..529654a3b538 100644 --- a/devtools/client/sourceeditor/codemirror/keymap/vim.js +++ b/devtools/client/sourceeditor/codemirror/keymap/vim.js @@ -841,7 +841,7 @@ } function handleKeyNonInsertMode() { - if (handleMacroRecording() || handleEsc()) { return true; }; + if (handleMacroRecording() || handleEsc()) { return true; } var keys = vim.inputState.keyBuffer = vim.inputState.keyBuffer + key; if (/^[1-9]\d*$/.test(keys)) { return true; } @@ -1426,7 +1426,7 @@ } else { if (vim.visualMode) { showPrompt(cm, { onClose: onPromptClose, prefix: ':', value: '\'<,\'>', - onKeyDown: onPromptKeyDown}); + onKeyDown: onPromptKeyDown, selectValueOnOpen: false}); } else { showPrompt(cm, { onClose: onPromptClose, prefix: ':', onKeyDown: onPromptKeyDown}); @@ -3683,7 +3683,15 @@ } } function splitBySlash(argString) { - var slashes = findUnescapedSlashes(argString) || []; + return splitBySeparator(argString, '/'); + } + + function findUnescapedSlashes(argString) { + return findUnescapedSeparators(argString, '/'); + } + + function splitBySeparator(argString, separator) { + var slashes = findUnescapedSeparators(argString, separator) || []; if (!slashes.length) return []; var tokens = []; // in case of strings like foo/bar @@ -3695,12 +3703,15 @@ return tokens; } - function findUnescapedSlashes(str) { + function findUnescapedSeparators(str, separator) { + if (!separator) + separator = '/'; + var escapeNextChar = false; var slashes = []; for (var i = 0; i < str.length; i++) { var c = str.charAt(i); - if (!escapeNextChar && c == '/') { + if (!escapeNextChar && c == separator) { slashes.push(i); } escapeNextChar = !escapeNextChar && (c == '\\'); @@ -4566,7 +4577,7 @@ 'any other getSearchCursor implementation.'); } var argString = params.argString; - var tokens = argString ? splitBySlash(argString) : []; + var tokens = argString ? splitBySeparator(argString, argString[0]) : []; var regexPart, replacePart = '', trailing, flagsPart, count; var confirm = false; // Whether to confirm each replace. var global = false; // True to replace all instances on a line, false to replace only 1. @@ -4610,7 +4621,7 @@ global = true; flagsPart.replace('g', ''); } - regexPart = regexPart + '/' + flagsPart; + regexPart = regexPart.replace(/\//g, "\\/") + '/' + flagsPart; } } if (regexPart) { @@ -4826,7 +4837,7 @@ } if (!confirm) { replaceAll(); - if (callback) { callback(); }; + if (callback) { callback(); } return; } showPrompt(cm, { @@ -4962,7 +4973,7 @@ exitInsertMode(cm); } } - }; + } macroModeState.isPlaying = false; } @@ -5166,7 +5177,7 @@ exitInsertMode(cm); } macroModeState.isPlaying = false; - }; + } function repeatInsertModeChanges(cm, changes, repeat) { function keyHandler(binding) { diff --git a/devtools/client/sourceeditor/codemirror/lib/codemirror.js b/devtools/client/sourceeditor/codemirror/lib/codemirror.js index f22567442124..e942fe63728b 100644 --- a/devtools/client/sourceeditor/codemirror/lib/codemirror.js +++ b/devtools/client/sourceeditor/codemirror/lib/codemirror.js @@ -5212,7 +5212,8 @@ function makeChangeInner(doc, change) { // Revert a change stored in a document's history. function makeChangeFromHistory(doc, type, allowSelectionOnly) { - if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) { return } + var suppress = doc.cm && doc.cm.state.suppressEdits + if (suppress && !allowSelectionOnly) { return } var hist = doc.history, event, selAfter = doc.sel var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done @@ -5237,8 +5238,10 @@ function makeChangeFromHistory(doc, type, allowSelectionOnly) { return } selAfter = event - } - else { break } + } else if (suppress) { + source.push(event) + return + } else { break } } // Build up a reverse change object to add to the opposite history @@ -5714,7 +5717,7 @@ function addLineWidget(doc, handle, node, options) { } return true }) - signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)) + if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)) } return widget } @@ -9650,7 +9653,7 @@ CodeMirror.fromTextArea = fromTextArea addLegacyProps(CodeMirror) -CodeMirror.version = "5.34.0" +CodeMirror.version = "5.35.0" return CodeMirror; diff --git a/devtools/client/sourceeditor/codemirror/mode/clike/clike.js b/devtools/client/sourceeditor/codemirror/mode/clike/clike.js index b89ddf0d59bf..da7a6bf5b669 100644 --- a/devtools/client/sourceeditor/codemirror/mode/clike/clike.js +++ b/devtools/client/sourceeditor/codemirror/mode/clike/clike.js @@ -374,7 +374,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { blockKeywords: words("case do else for if switch while struct"), defKeywords: words("struct"), typeFirstDefinitions: true, - atoms: words("null true false"), + atoms: words("NULL true false"), hooks: {"#": cppHook, "*": pointerHook}, modeProps: {fold: ["brace", "include"]} }); @@ -390,7 +390,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { blockKeywords: words("catch class do else finally for if struct switch try while"), defKeywords: words("class namespace struct enum union"), typeFirstDefinitions: true, - atoms: words("true false null"), + atoms: words("true false NULL"), dontIndentStatements: /^template$/, isIdentifierChar: /[\w\$_~\xa1-\uffff]/, hooks: { @@ -597,22 +597,24 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { name: "clike", keywords: words( /*keywords*/ - "package as typealias class interface this super val " + - "var fun for is in This throw return " + + "package as typealias class interface this super val operator " + + "var fun for is in This throw return annotation " + "break continue object if else while do try when !in !is as? " + /*soft keywords*/ "file import where by get set abstract enum open inner override private public internal " + "protected catch finally out final vararg reified dynamic companion constructor init " + "sealed field property receiver param sparam lateinit data inline noinline tailrec " + - "external annotation crossinline const operator infix suspend actual expect" + "external annotation crossinline const operator infix suspend actual expect setparam" ), types: words( /* package java.lang */ "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " + "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " + "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " + - "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void" + "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void Annotation Any BooleanArray " + + "ByteArray Char CharArray DeprecationLevel DoubleArray Enum FloatArray Function Int IntArray Lazy " + + "LazyThreadSafetyMode LongArray Nothing ShortArray Unit" ), intendSwitch: false, indentStatements: false, diff --git a/devtools/client/sourceeditor/codemirror/mode/javascript/javascript.js b/devtools/client/sourceeditor/codemirror/mode/javascript/javascript.js index 9eb50ba974e8..52da9e23532a 100644 --- a/devtools/client/sourceeditor/codemirror/mode/javascript/javascript.js +++ b/devtools/client/sourceeditor/codemirror/mode/javascript/javascript.js @@ -400,6 +400,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "{") return contCommasep(objprop, "}", null, maybeop); if (type == "quasi") return pass(quasi, maybeop); if (type == "new") return cont(maybeTarget(noComma)); + if (type == "import") return cont(expression); return cont(); } function maybeexpression(type) { @@ -594,7 +595,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } function afterType(type, value) { if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) - if (value == "|" || type == ".") return cont(typeexpr) + if (value == "|" || type == "." || value == "&") return cont(typeexpr) if (type == "[") return cont(expect("]"), afterType) if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) } } @@ -637,7 +638,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function maybeelse(type, value) { if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex); } - function forspec(type) { + function forspec(type, value) { + if (value == "await") return cont(forspec); if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex); } function forspec1(type) { @@ -726,6 +728,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } function afterImport(type) { if (type == "string") return cont(); + if (type == "(") return pass(expression); return pass(importSpec, maybeMoreImports, maybeFrom); } function importSpec(type, value) { diff --git a/devtools/client/sourceeditor/test/codemirror/mode/javascript/test.js b/devtools/client/sourceeditor/test/codemirror/mode/javascript/test.js index 14a5183cab41..f770bd77f6b1 100644 --- a/devtools/client/sourceeditor/test/codemirror/mode/javascript/test.js +++ b/devtools/client/sourceeditor/test/codemirror/mode/javascript/test.js @@ -63,6 +63,12 @@ MT("import_trailing_comma", "[keyword import] {[def foo], [def bar],} [keyword from] [string 'baz']") + MT("import_dynamic", + "[keyword import]([string 'baz']).[property then]") + + MT("import_dynamic", + "[keyword const] [def t] [operator =] [keyword import]([string 'baz']).[property then]") + MT("const", "[keyword function] [def f]() {", " [keyword const] [[ [def a], [def b] ]] [operator =] [[ [number 1], [number 2] ]];", @@ -71,6 +77,9 @@ MT("for/of", "[keyword for]([keyword let] [def of] [keyword of] [variable something]) {}"); + MT("for await", + "[keyword for] [keyword await]([keyword let] [def of] [keyword of] [variable something]) {}"); + MT("generator", "[keyword function*] [def repeat]([def n]) {", " [keyword for]([keyword var] [def i] [operator =] [number 0]; [variable-2 i] [operator <] [variable-2 n]; [operator ++][variable-2 i])", diff --git a/devtools/client/sourceeditor/test/codemirror/test.js b/devtools/client/sourceeditor/test/codemirror/test.js index 87615d06300b..415dd9f0bc5d 100644 --- a/devtools/client/sourceeditor/test/codemirror/test.js +++ b/devtools/client/sourceeditor/test/codemirror/test.js @@ -2330,7 +2330,7 @@ testCM("lineSeparator", function(cm) { lineSeparator: "\n"}); var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/ -var getChar = function (noExtending) { var res; do {res = String.fromCharCode(Math.floor(Math.random()*0x8ac)); } while ([0x90].includes(res.charCodeAt(0)) || (noExtending && extendingChars.test(res))); return res } +var getChar = function (noExtending) { var res; do {res = String.fromCharCode(Math.floor(Math.random()*0x8ac)); } while ([0x90].indexOf(res.charCodeAt(0)) != -1 || (noExtending && extendingChars.test(res))); return res } var getString = function (n) { var res = getChar(true); while (--n > 0) res += getChar(); return res } function makeItWrapAfter(cm, pos) { @@ -2356,7 +2356,7 @@ function testMoveBidi(str) { var steps = str.length - countIf(str.split(""), function(ch) { return extendingChars.test(ch) }); var lineBreaks = {} lineBreaks[6 - countIf(str.substr(0, 5).split(""), function(ch) { return extendingChars.test(ch) })] = 'w'; - if (str.includes("\n")) { + if (str.indexOf("\n") != -1) { lineBreaks[steps - 2] = 'n'; } diff --git a/devtools/client/sourceeditor/test/codemirror/vim_test.js b/devtools/client/sourceeditor/test/codemirror/vim_test.js index e5bf1f208135..6433134c21d6 100644 --- a/devtools/client/sourceeditor/test/codemirror/vim_test.js +++ b/devtools/client/sourceeditor/test/codemirror/vim_test.js @@ -153,7 +153,7 @@ function testVim(name, run, opts, expectedFail) { // Record for insert mode. if (handled == "handled" && cm.state.vim.insertMode && arguments[i] != 'Esc') { var lastChange = CodeMirror.Vim.getVimGlobalState_().macroModeState.lastInsertModeChanges; - if (lastChange && (key.includes('Delete') || key.includes('Backspace'))) { + if (lastChange && (key.indexOf('Delete') != -1 || key.indexOf('Backspace') != -1)) { lastChange.changes.push(new CodeMirror.Vim.InsertModeKey(key)); } } @@ -255,7 +255,7 @@ function testJumplist(name, keys, endPos, startPos, dialog) { helpers.doKeys.apply(null, keys); helpers.assertCursorAt(endPos); }, {value: jumplistScene}); -}; +} testJumplist('jumplist_H', ['H', ''], [5,2], [5,2]); testJumplist('jumplist_M', ['M', ''], [2,2], [2,2]); testJumplist('jumplist_L', ['L', ''], [2,2], [2,2]); @@ -299,15 +299,15 @@ function testMotion(name, keys, endPos, startPos) { helpers.doKeys(keys); helpers.assertCursorAt(endPos); }); -}; +} function makeCursor(line, ch) { return new Pos(line, ch); -}; +} function offsetCursor(cur, offsetLine, offsetCh) { return new Pos(cur.line + offsetLine, cur.ch + offsetCh); -}; +} // Motion tests testMotion('|', '|', makeCursor(0, 0), makeCursor(0,4)); @@ -3648,6 +3648,11 @@ testVim('ex_substitute_same_line', function(cm, vim, helpers) { helpers.doEx('s/one/two/g'); eq('one one\n two two', cm.getValue()); }, { value: 'one one\n one one'}); +testVim('ex_substitute_alternate_separator', function(cm, vim, helpers) { + cm.setCursor(1, 0); + helpers.doEx('s#o/e#two#g'); + eq('o/e o/e\n two two', cm.getValue()); +}, { value: 'o/e o/e\n o/e o/e'}); testVim('ex_substitute_full_file', function(cm, vim, helpers) { cm.setCursor(1, 0); helpers.doEx('%s/one/two/g'); @@ -3904,7 +3909,7 @@ function testSubstituteConfirm(name, command, initialValue, expectedValue, keys, cm.openDialog = savedOpenDialog; } }, { value: initialValue }); -}; +} testSubstituteConfirm('ex_substitute_confirm_emptydoc', '%s/x/b/c', '', '', '', makeCursor(0, 0)); testSubstituteConfirm('ex_substitute_confirm_nomatch', diff --git a/devtools/server/tests/unit/test_blackboxing-01.js b/devtools/server/tests/unit/test_blackboxing-01.js index 7d002f304e9e..00bc8bd077e8 100644 --- a/devtools/server/tests/unit/test_blackboxing-01.js +++ b/devtools/server/tests/unit/test_blackboxing-01.js @@ -96,7 +96,7 @@ function evalCode() { /* eslint-disable */ Components.utils.evalInSandbox( "" + function doStuff(k) { // line 1 - let arg = 15; // line 2 - Step in here + var arg = 15; // line 2 - Step in here k(arg); // line 3 }, // line 4 gDebuggee, diff --git a/devtools/server/tests/unit/test_stepping-08.js b/devtools/server/tests/unit/test_stepping-08.js index 6a82e890013e..a5a69a0327e0 100644 --- a/devtools/server/tests/unit/test_stepping-08.js +++ b/devtools/server/tests/unit/test_stepping-08.js @@ -60,8 +60,8 @@ function evaluateTestCode() { } // 4 // 5 function innerFunction() { // 6 - let x = 0; // 7 - let y = 72; // 8 + var x = 0; // 7 + var y = 72; // 8 return x+y; // 9 } // 10 outerFunction(); // 11 diff --git a/dom/bindings/moz.build b/dom/bindings/moz.build index b814676bbf06..dfe45219913b 100644 --- a/dom/bindings/moz.build +++ b/dom/bindings/moz.build @@ -121,7 +121,7 @@ SOURCES += [ # rule stated in the test/ directory, but it's the only way this will work. # Test classes are only built in debug mode, and all tests requiring use of # them are only run in debug mode. -if CONFIG['MOZ_DEBUG']: +if CONFIG['MOZ_DEBUG'] and CONFIG['ENABLE_TESTS']: EXPORTS.mozilla.dom += [ "test/TestFunctions.h", "test/TestInterfaceIterableDouble.h", diff --git a/dom/cache/test/mochitest/browser_cache_pb_window.js b/dom/cache/test/mochitest/browser_cache_pb_window.js index 81619e5e57f2..09ac5c45df23 100644 --- a/dom/cache/test/mochitest/browser_cache_pb_window.js +++ b/dom/cache/test/mochitest/browser_cache_pb_window.js @@ -70,6 +70,30 @@ function testKeys(browser) { }); } +function testOpen_worker(browser) { + return ContentTask.spawn(browser, {}, function() { + let workerFunctionString = function () { + caches.open("pb-worker-cache").then(function(cacheObject) { + postMessage(cacheObject.toString()); + }, function (reason) { + postMessage(reason.name); + }); + }.toString(); + let workerBlobURL = content.URL.createObjectURL( + new Blob(['(', workerFunctionString, ')()'], + { type : 'application/javascript' })); + let worker = new content.Worker(workerBlobURL); + content.URL.revokeObjectURL(workerBlobURL); + return new Promise(function(resolve, reject) { + worker.addEventListener("message", function (e) { + let isGood = (e.data === "SecurityError"); + ok(isGood, "caches.open() should throw SecurityError from worker"); + isGood ? resolve() : reject(); + }); + }); + }); +} + function test() { let privateWin, privateTab; waitForExplicitFinish(); @@ -88,6 +112,7 @@ function test() { testOpen(privateTab.linkedBrowser), testDelete(privateTab.linkedBrowser), testKeys(privateTab.linkedBrowser), + testOpen_worker(privateTab.linkedBrowser), ]); }).then(() => { return BrowserTestUtils.closeWindow(privateWin); diff --git a/dom/fetch/FetchDriver.cpp b/dom/fetch/FetchDriver.cpp index f00ab1029074..fc5619dd0e43 100644 --- a/dom/fetch/FetchDriver.cpp +++ b/dom/fetch/FetchDriver.cpp @@ -797,6 +797,11 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest, // In that case we will get a simulated OnStartRequest() and then the real // channel will call in with an errored OnStartRequest(). + if (!mChannel) { + MOZ_ASSERT(!mObserver); + return NS_BINDING_ABORTED; + } + nsresult rv; aRequest->GetStatus(&rv); if (NS_FAILED(rv)) { diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index d5e58ff02567..a4168e825a54 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -1034,7 +1034,7 @@ WEBIDL_FILES += [ # We only expose our prefable test interfaces in debug builds, just to be on # the safe side. -if CONFIG['MOZ_DEBUG']: +if CONFIG['MOZ_DEBUG'] and CONFIG['ENABLE_TESTS']: WEBIDL_FILES += ['TestFunctions.webidl', 'TestInterfaceJS.webidl', 'TestInterfaceJSDictionaries.webidl', diff --git a/dom/xul/nsXULPrototypeDocument.cpp b/dom/xul/nsXULPrototypeDocument.cpp index 55073fb45317..68d1148ed828 100644 --- a/dom/xul/nsXULPrototypeDocument.cpp +++ b/dom/xul/nsXULPrototypeDocument.cpp @@ -108,27 +108,25 @@ NS_NewXULPrototypeDocument(nsXULPrototypeDocument** aResult) NS_IMETHODIMP nsXULPrototypeDocument::Read(nsIObjectInputStream* aStream) { - nsresult rv; - nsCOMPtr supports; - rv = aStream->ReadObject(true, getter_AddRefs(supports)); + nsresult rv = aStream->ReadObject(true, getter_AddRefs(supports)); + if (NS_FAILED(rv)) { + return rv; + } mURI = do_QueryInterface(supports); uint32_t count, i; nsCOMPtr styleOverlayURI; - nsresult tmp = aStream->Read32(&count); - if (NS_FAILED(tmp)) { - return tmp; - } + rv = aStream->Read32(&count); if (NS_FAILED(rv)) { return rv; } for (i = 0; i < count; ++i) { - tmp = aStream->ReadObject(true, getter_AddRefs(supports)); - if (NS_FAILED(tmp)) { - rv = tmp; + rv = aStream->ReadObject(true, getter_AddRefs(supports)); + if (NS_FAILED(rv)) { + return rv; } styleOverlayURI = do_QueryInterface(supports); mStyleSheetReferences.AppendObject(styleOverlayURI); @@ -136,12 +134,11 @@ nsXULPrototypeDocument::Read(nsIObjectInputStream* aStream) // nsIPrincipal mNodeInfoManager->mPrincipal - nsCOMPtr principal; - tmp = aStream->ReadObject(true, getter_AddRefs(supports)); - principal = do_QueryInterface(supports); - if (NS_FAILED(tmp)) { - rv = tmp; + rv = aStream->ReadObject(true, getter_AddRefs(supports)); + if (NS_FAILED(rv)) { + return rv; } + nsCOMPtr principal = do_QueryInterface(supports); // Better safe than sorry.... mNodeInfoManager->SetDocumentPrincipal(principal); @@ -150,45 +147,45 @@ nsXULPrototypeDocument::Read(nsIObjectInputStream* aStream) // mozilla::dom::NodeInfo table nsTArray> nodeInfos; - tmp = aStream->Read32(&count); - if (NS_FAILED(tmp)) { - rv = tmp; + rv = aStream->Read32(&count); + if (NS_FAILED(rv)) { + return rv; } nsAutoString namespaceURI, prefixStr, localName; bool prefixIsNull; RefPtr prefix; for (i = 0; i < count; ++i) { - tmp = aStream->ReadString(namespaceURI); - if (NS_FAILED(tmp)) { - rv = tmp; + rv = aStream->ReadString(namespaceURI); + if (NS_FAILED(rv)) { + return rv; } - tmp = aStream->ReadBoolean(&prefixIsNull); - if (NS_FAILED(tmp)) { - rv = tmp; + rv = aStream->ReadBoolean(&prefixIsNull); + if (NS_FAILED(rv)) { + return rv; } if (prefixIsNull) { prefix = nullptr; } else { - tmp = aStream->ReadString(prefixStr); - if (NS_FAILED(tmp)) { - rv = tmp; + rv = aStream->ReadString(prefixStr); + if (NS_FAILED(rv)) { + return rv; } prefix = NS_Atomize(prefixStr); } - tmp = aStream->ReadString(localName); - if (NS_FAILED(tmp)) { - rv = tmp; + rv = aStream->ReadString(localName); + if (NS_FAILED(rv)) { + return rv; } RefPtr nodeInfo; // Using UINT16_MAX here as we don't know which nodeinfos will be // used for attributes and which for elements. And that doesn't really // matter. - tmp = mNodeInfoManager->GetNodeInfo(localName, prefix, namespaceURI, - UINT16_MAX, - getter_AddRefs(nodeInfo)); - if (NS_FAILED(tmp)) { - rv = tmp; + rv = mNodeInfoManager->GetNodeInfo(localName, prefix, namespaceURI, + UINT16_MAX, + getter_AddRefs(nodeInfo)); + if (NS_FAILED(rv)) { + return rv; } nodeInfos.AppendElement(nodeInfo); } @@ -196,41 +193,36 @@ nsXULPrototypeDocument::Read(nsIObjectInputStream* aStream) // Document contents uint32_t type; while (NS_SUCCEEDED(rv)) { - tmp = aStream->Read32(&type); - if (NS_FAILED(tmp)) { - rv = tmp; + rv = aStream->Read32(&type); + if (NS_FAILED(rv)) { + return rv; break; } if ((nsXULPrototypeNode::Type)type == nsXULPrototypeNode::eType_PI) { RefPtr pi = new nsXULPrototypePI(); - tmp = pi->Deserialize(aStream, this, mURI, &nodeInfos); - if (NS_FAILED(tmp)) { - rv = tmp; + rv = pi->Deserialize(aStream, this, mURI, &nodeInfos); + if (NS_FAILED(rv)) { + return rv; } - tmp = AddProcessingInstruction(pi); - if (NS_FAILED(tmp)) { - rv = tmp; + rv = AddProcessingInstruction(pi); + if (NS_FAILED(rv)) { + return rv; } } else if ((nsXULPrototypeNode::Type)type == nsXULPrototypeNode::eType_Element) { - tmp = mRoot->Deserialize(aStream, this, mURI, &nodeInfos); - if (NS_FAILED(tmp)) { - rv = tmp; + rv = mRoot->Deserialize(aStream, this, mURI, &nodeInfos); + if (NS_FAILED(rv)) { + return rv; } break; } else { NS_NOTREACHED("Unexpected prototype node type"); - rv = NS_ERROR_FAILURE; - break; + return NS_ERROR_FAILURE; } } - tmp = NotifyLoadDone(); - if (NS_FAILED(tmp)) { - rv = tmp; - } - return rv; + return NotifyLoadDone(); } static nsresult diff --git a/js/public/StructuredClone.h b/js/public/StructuredClone.h index 5244f45a5fcf..f26d2c750f93 100644 --- a/js/public/StructuredClone.h +++ b/js/public/StructuredClone.h @@ -20,16 +20,143 @@ #include "js/Value.h" #include "js/Vector.h" +/* + * API for safe passing of structured data, HTML 2018 Feb 21 section 2.7. + * + * + * This is a serialization scheme for JS values, somewhat like JSON. It + * preserves some aspects of JS objects (strings, numbers, own data properties + * with string keys, array elements) but not others (methods, getters and + * setters, prototype chains). Unlike JSON, structured data: + * + * - can contain cyclic references. + * + * - handles Maps, Sets, and some other object types. + * + * - supports *transferring* objects of certain types from one realm to + * another, rather than cloning them. + * + * - is specified by a living standard, and continues to evolve. + * + * - is encoded in a nonstandard binary format, and is never exposed to Web + * content in its serialized form. It's used internally by the browser to + * send data from one thread/realm/domain to another, not across the + * network. + */ + struct JSStructuredCloneReader; struct JSStructuredCloneWriter; -// API for the HTML5 internal structured cloning algorithm. +/** + * The structured-clone serialization format version number. + * + * When serialized data is stored as bytes, e.g. in your Firefox profile, later + * versions of the engine may have to read it. When you upgrade Firefox, we + * don't crawl through your whole profile converting all saved data from the + * previous version of the serialization format to the latest version. So it is + * normal to have data in old formats stored in your profile. + * + * The JS engine can *write* data only in the current format version. + * + * It can *read* any data written in the current version, and data written for + * DifferentProcess scope in earlier versions. + * + * + * ## When to bump this version number + * + * When making a change so drastic that the JS engine needs to know whether + * it's reading old or new serialized data in order to handle both correctly, + * increment this version number. Make sure the engine can still read all + * old data written with previous versions. + * + * If StructuredClone.cpp doesn't contain code that distinguishes between + * version 8 and version 9, there should not be a version 9. + * + * Do not increment for changes that only affect SameProcess encoding. + * + * Increment only for changes that would otherwise break old serialized data. + * Do not increment for new data types. (Rationale: Modulo bugs, older versions + * of the JS engine can already correctly throw errors when they encounter new, + * unrecognized features. A version number bump does not actually help them.) + */ +#define JS_STRUCTURED_CLONE_VERSION 8 namespace JS { +/** + * Indicates the "scope of validity" of serialized data. + * + * Writing plain JS data produces an array of bytes that can be copied and + * read in another process or whatever. The serialized data is Plain Old Data. + * However, HTML also supports `Transferable` objects, which, when cloned, can + * be moved from the source object into the clone, like when you take a + * photograph of someone and it steals their soul. + * See . + * We support cloning and transferring objects of many types. + * + * For example, when we transfer an ArrayBuffer (within a process), we "detach" + * the ArrayBuffer, embed the raw buffer pointer in the serialized data, and + * later install it in a new ArrayBuffer in the destination realm. Ownership + * of that buffer memory is transferred from the original ArrayBuffer to the + * serialized data and then to the clone. + * + * This only makes sense within a single address space. When we transfer an + * ArrayBuffer to another process, the contents of the buffer must be copied + * into the serialized data. (The original ArrayBuffer is still detached, + * though, for consistency; in some cases the caller shouldn't know or care if + * the recipient is in the same process.) + * + * ArrayBuffers are actually a lucky case; some objects (like MessagePorts) + * can't reasonably be stored by value in serialized data -- it's pointers or + * nothing. + * + * So there is a tradeoff between scope of validity -- how far away the + * serialized data may be sent and still make sense -- and efficiency or + * features. The read and write algorithms therefore take an argument of this + * type, allowing the user to control those trade-offs. + */ enum class StructuredCloneScope : uint32_t { + /** + * The most restrictive scope, with greatest efficiency and features. + * + * When writing, this means we're writing for an audience in the same + * process and same thread. The caller promises that the serialized data + * will **not** be shipped off to a different thread/process or stored in a + * database. It's OK to produce serialized data that contains pointers. In + * Rust terms, the serialized data will be treated as `!Send`. + * + * When reading, this means: Accept transferred objects and buffers + * (pointers). The caller promises that the serialized data was written + * using this API (otherwise, the serialized data may contain bogus + * pointers, leading to undefined behavior). + */ SameProcessSameThread, + + /** + * When writing, this means: The caller promises that the serialized data + * will **not** be shipped off to a different process or stored in a + * database. However, it may be shipped to another thread. It's OK to + * produce serialized data that contains pointers to data that is safe to + * send across threads, such as array buffers. In Rust terms, the + * serialized data will be treated as `Send` but not `Copy`. + * + * When reading, this means the same thing as SameProcessSameThread; + * the distinction only matters when writing. + */ SameProcessDifferentThread, + + /** + * The broadest scope. + * + * When writing, this means we're writing for an audience in a different + * process. Produce serialized data that can be sent to other processes, + * bitwise copied, or even stored as bytes in a database and read by later + * versions of Firefox years from now. Transferable objects are limited to + * ArrayBuffers, whose contents are copied into the serialized data (rather + * than just writing a pointer). + * + * When reading, this means: Do not accept pointers. + */ DifferentProcess }; @@ -168,12 +295,6 @@ typedef bool (*TransferStructuredCloneOp)(JSContext* cx, typedef void (*FreeTransferStructuredCloneOp)(uint32_t tag, JS::TransferableOwnership ownership, void* content, uint64_t extraData, void* closure); -// The maximum supported structured-clone serialization format version. -// Increment this when anything at all changes in the serialization format. -// (Note that this does not need to be bumped for Transferable-only changes, -// since they are never saved to persistent storage.) -#define JS_STRUCTURED_CLONE_VERSION 8 - struct JSStructuredCloneCallbacks { ReadStructuredCloneOp read; WriteStructuredCloneOp write; @@ -257,7 +378,11 @@ public: using BufferList::BufferList; }; -/** Note: if the *data contains transferable objects, it can be read only once. */ +/** + * Implements StructuredDeserialize and StructuredDeserializeWithTransfer. + * + * Note: If `data` contains transferable objects, it can be read only once. + */ JS_PUBLIC_API(bool) JS_ReadStructuredClone(JSContext* cx, JSStructuredCloneData& data, uint32_t version, JS::StructuredCloneScope scope, @@ -265,6 +390,9 @@ JS_ReadStructuredClone(JSContext* cx, JSStructuredCloneData& data, uint32_t vers const JSStructuredCloneCallbacks* optionalCallbacks, void* closure); /** + * Implements StructuredSerialize, StructuredSerializeForStorage, and + * StructuredSerializeWithTransfer. + * * Note: If the scope is DifferentProcess then the cloneDataPolicy must deny * shared-memory objects, or an error will be signaled if a shared memory object * is seen. diff --git a/js/src/builtin/SelfHostingDefines.h b/js/src/builtin/SelfHostingDefines.h index 5ca1b7514d77..81e51aa34875 100644 --- a/js/src/builtin/SelfHostingDefines.h +++ b/js/src/builtin/SelfHostingDefines.h @@ -23,6 +23,9 @@ // NB: keep this in sync with the copy in vm/ArgumentsObject.h. #define MAX_ARGS_LENGTH (500 * 1000) +// NB: keep this in sync with the copy in vm/String.h. +#define MAX_STRING_LENGTH ((1 << 28) - 1) + // Spread non-empty argument list of up to 15 elements. #define SPREAD(v, n) SPREAD_##n(v) #define SPREAD_1(v) v[0] diff --git a/js/src/builtin/String.js b/js/src/builtin/String.js index 8708d16394ae..572dfc88818b 100644 --- a/js/src/builtin/String.js +++ b/js/src/builtin/String.js @@ -68,8 +68,7 @@ function String_generic_match(thisValue, regexp) { * A helper function implementing the logic for both String.prototype.padStart * and String.prototype.padEnd as described in ES7 Draft March 29, 2016 */ -function String_pad(maxLength, fillString, padEnd = false) { - +function String_pad(maxLength, fillString, padEnd) { // Steps 1-2. RequireObjectCoercible(this); let str = ToString(this); @@ -83,21 +82,28 @@ function String_pad(maxLength, fillString, padEnd = false) { return str; // Steps 6-7. - let filler = fillString === undefined ? " " : ToString(fillString); + assert(fillString !== undefined, "never called when fillString is undefined"); + let filler = ToString(fillString); // Step 8. if (filler === "") return str; + // Throw an error if the final string length exceeds the maximum string + // length. Perform this check early so we can use int32 operations below. + if (intMaxLength > MAX_STRING_LENGTH) + ThrowRangeError(JSMSG_RESULTING_STRING_TOO_LARGE); + // Step 9. let fillLen = intMaxLength - strLen; // Step 10. + // Perform an int32 division to ensure String_repeat is not called with a + // double to avoid repeated bailouts in ToInteger. let truncatedStringFiller = callFunction(String_repeat, filler, - fillLen / filler.length); + (fillLen / filler.length) | 0); - truncatedStringFiller += callFunction(String_substr, filler, 0, - fillLen % filler.length); + truncatedStringFiller += Substring(filler, 0, fillLen % filler.length); // Step 11. if (padEnd === true) @@ -503,11 +509,14 @@ function String_repeat(count) { if (n < 0) ThrowRangeError(JSMSG_NEGATIVE_REPETITION_COUNT); - if (!(n * S.length < (1 << 28))) + // Inverted condition to handle |Infinity * 0 = NaN| correctly. + if (!(n * S.length <= MAX_STRING_LENGTH)) ThrowRangeError(JSMSG_RESULTING_STRING_TOO_LARGE); // Communicate |n|'s possible range to the compiler. - n = n & ((1 << 28) - 1); + assert((MAX_STRING_LENGTH & (MAX_STRING_LENGTH + 1)) === 0, + "MAX_STRING_LENGTH can be used as a bitmask"); + n = n & MAX_STRING_LENGTH; // Steps 8-9. var T = ""; diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index a6b7b010fe27..e0cb7c5d1947 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -2911,7 +2911,7 @@ const Class CloneBufferObject::class_ = { const JSPropertySpec CloneBufferObject::props_[] = { JS_PSGS("clonebuffer", getCloneBuffer, setCloneBuffer, 0), - JS_PSG("arraybuffer", getCloneBufferAsArrayBuffer, 0), + JS_PSGS("arraybuffer", getCloneBufferAsArrayBuffer, setCloneBuffer, 0), JS_PS_END }; diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 59fb359e84a1..fe0969fae6da 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -895,7 +895,7 @@ BytecodeEmitter::EmitterScope::enterLexical(BytecodeEmitter* bce, ScopeKind kind if (!ensureCache(bce)) return false; - // Marks all names as closed over if the the context requires it. This + // Marks all names as closed over if the context requires it. This // cannot be done in the Parser as we may not know if the context requires // all bindings to be closed over until after parsing is finished. For // example, legacy generators require all bindings to be closed over but @@ -6831,24 +6831,12 @@ BytecodeEmitter::emitLexicalScope(ParseNode* pn) if (pn->isEmptyScope()) return emitLexicalScopeBody(body); - // Update line number notes before emitting TDZ poison in - // EmitterScope::enterLexical to avoid spurious pausing on seemingly - // non-effectful lines in Debugger. - // - // For example, consider the following code. - // - // L1: { - // L2: let x = 42; - // L3: } - // - // If line number notes were not updated before the TDZ poison, the TDZ - // poison bytecode sequence of 'uninitialized; initlexical' will have line - // number L1, and the Debugger will pause there. + // We are about to emit some bytecode for what the spec calls "declaration + // instantiation". Assign these instructions to the opening `{` of the + // block. (Using the location of each declaration we're instantiating is + // too weird when stepping in the debugger.) if (!ParseNodeRequiresSpecialLineNumberNotes(body)) { - ParseNode* pnForPos = body; - if (body->isKind(ParseNodeKind::StatementList) && body->pn_head) - pnForPos = body->pn_head; - if (!updateLineNumberNotes(pnForPos->pn_pos.begin)) + if (!updateSourceCoordNotes(pn->pn_pos.begin)) return false; } diff --git a/js/src/jit-test/lib/stepping.js b/js/src/jit-test/lib/stepping.js new file mode 100644 index 000000000000..122fd9631936 --- /dev/null +++ b/js/src/jit-test/lib/stepping.js @@ -0,0 +1,32 @@ +// Test that stepping through a function stops at the expected lines. +// `script` is a string, some JS code that evaluates to a function. +// `expected` is the array of line numbers where stepping is expected to stop +// when we call the function. +function testStepping(script, expected) { + let g = newGlobal(); + let f = g.eval(script); + + let log = []; + function maybePause(frame) { + let previousLine = log[log.length - 1]; // note: may be undefined + let line = frame.script.getOffsetLocation(frame.offset).lineNumber; + if (line !== previousLine) + log.push(line); + } + + let dbg = new Debugger(g); + dbg.onEnterFrame = frame => { + // Log this pause (before the first instruction of the function). + maybePause(frame); + + // Log future pauses in the same stack frame. + frame.onStep = function() { maybePause(this); }; + + // Now disable this hook so that we step over function calls, not into them. + dbg.onEnterFrame = undefined; + }; + + f(); + + assertEq(log.join(","), expected.join(",")); +} diff --git a/js/src/jit-test/tests/debug/Frame-onStep-19.js b/js/src/jit-test/tests/debug/Frame-onStep-19.js index 3997b21182cd..019c026a8e49 100644 --- a/js/src/jit-test/tests/debug/Frame-onStep-19.js +++ b/js/src/jit-test/tests/debug/Frame-onStep-19.js @@ -6,48 +6,25 @@ // But users don't actually want to see that happen when they're stepping. // It's super confusing. -function runTest(script, expected) { - let g = newGlobal(); - g.eval(script); +load(libdir + "stepping.js"); - let dbg = new Debugger(g); - let log = []; - dbg.onEnterFrame = frame => { - let previousLine = undefined; - frame.onStep = function() { - let line = this.script.getOffsetLocation(this.offset).lineNumber; - if (line != previousLine) { - log.push(line); - previousLine = line; - } - }; - - // Now disable this hook so that we step over function calls, not into them. - dbg.onEnterFrame = undefined; - }; - - g.f(); - - assertEq(log.join(","), expected.join(",")); -} - -runTest( +testStepping( `\ - var f = (function() { // line 1 + (function() { // line 1 let x = 1; // line 2 funcb("funcb"); // line 3 function funcb(msg) { // line 4 console.log(msg) } - }); // line 7 + }) // line 7 `, - [2, 3, 7]); + [1, 2, 3, 7]); // Stopping at the ClassDeclaration on line 8 is fine. For that matter, // stopping on line 5 wouldn't be so bad if we did it after line 3 and before // line 8; alas, the actual order of execution is 5, 2, 3, 8... which is too // confusing. -runTest( +testStepping( `\ function f() { // 1 var x = 0; // 2 @@ -59,5 +36,6 @@ runTest( class Car {} // 8 return x; // 9 } // 10 + f `, - [2, 3, 8, 9, 10]); + [1, 2, 3, 8, 9, 10]); diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index bb78da3d47dd..6c482e6694f4 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -4532,9 +4532,16 @@ LIRGenerator::visitWasmBoundsCheck(MWasmBoundsCheck* ins) MDefinition* boundsCheckLimit = ins->boundsCheckLimit(); MOZ_ASSERT(boundsCheckLimit->type() == MIRType::Int32); - auto* lir = new(alloc()) LWasmBoundsCheck(useRegisterAtStart(index), - useRegisterAtStart(boundsCheckLimit)); - add(lir, ins); + + if (JitOptions.spectreIndexMasking) { + auto* lir = new(alloc()) LWasmBoundsCheck(useRegisterAtStart(index), + useRegister(boundsCheckLimit)); + defineReuseInput(lir, ins, 0); + } else { + auto* lir = new(alloc()) LWasmBoundsCheck(useRegisterAtStart(index), + useRegisterAtStart(boundsCheckLimit)); + add(lir, ins); + } #endif } diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index f318568dce75..1b5f5b7aa9f3 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -14236,6 +14236,9 @@ class MWasmBoundsCheck { // Bounds check is effectful: it throws for OOB. setGuard(); + + if (JitOptions.spectreIndexMasking) + setResultType(MIRType::Int32); } public: diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index 00b74fdcdf07..6d3aa79a5950 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -1470,8 +1470,10 @@ class MacroAssembler : public MacroAssemblerSpecific void wasmTrap(wasm::Trap trap, wasm::BytecodeOffset bytecodeOffset); - // Emit a bounds check against the wasm heap limit, jumping to 'label' if 'cond' holds. - // Required when WASM_HUGE_MEMORY is not defined. + // Emit a bounds check against the wasm heap limit, jumping to 'label' if + // 'cond' holds. Required when WASM_HUGE_MEMORY is not defined. If + // JitOptions.spectreMaskIndex is true, in speculative executions 'index' is + // saturated in-place to 'boundsCheckLimit'. template inline void wasmBoundsCheck(Condition cond, Register index, Register boundsCheckLimit, L label) DEFINED_ON(arm, arm64, mips32, mips64, x86); diff --git a/js/src/jit/WasmBCE.cpp b/js/src/jit/WasmBCE.cpp index 424c5441dc72..af5e5f44b684 100644 --- a/js/src/jit/WasmBCE.cpp +++ b/js/src/jit/WasmBCE.cpp @@ -15,6 +15,8 @@ using namespace mozilla; typedef js::HashMap, SystemAllocPolicy> LastSeenMap; +unsigned redundantCount = 0; + // The Wasm Bounds Check Elimination (BCE) pass looks for bounds checks // on SSA values that have already been checked. (in the same block or in a // dominating block). These bounds checks are redundant and thus eliminated. @@ -60,13 +62,25 @@ jit::EliminateBoundsChecks(MIRGenerator* mir, MIRGraph& graph) uint32_t(addr->toConstant()->toInt32()) < mir->minWasmHeapLength()) { bc->setRedundant(); + redundantCount++; + if (JitOptions.spectreIndexMasking) + bc->replaceAllUsesWith(addr); + else + MOZ_ASSERT(!bc->hasUses()); } else { LastSeenMap::AddPtr ptr = lastSeen.lookupForAdd(addr->id()); if (ptr) { - if (ptr->value()->block()->dominates(block)) + MDefinition* prevCheckOrPhi = ptr->value(); + if (prevCheckOrPhi->block()->dominates(block)) { bc->setRedundant(); + redundantCount++; + if (JitOptions.spectreIndexMasking) + bc->replaceAllUsesWith(prevCheckOrPhi); + else + MOZ_ASSERT(!bc->hasUses()); + } } else { if (!lastSeen.add(ptr, addr->id(), def)) return false; @@ -90,6 +104,13 @@ jit::EliminateBoundsChecks(MIRGenerator* mir, MIRGraph& graph) for (int i = 0, nOps = phi->numOperands(); i < nOps; i++) { MDefinition* src = phi->getOperand(i); + if (JitOptions.spectreIndexMasking) { + if (src->isWasmBoundsCheck()) + src = src->toWasmBoundsCheck()->index(); + } else { + MOZ_ASSERT(!src->isWasmBoundsCheck()); + } + LastSeenMap::Ptr checkPtr = lastSeen.lookup(src->id()); if (!checkPtr || !checkPtr->value()->block()->dominates(block)) { phiChecked = false; diff --git a/js/src/jit/arm/MacroAssembler-arm-inl.h b/js/src/jit/arm/MacroAssembler-arm-inl.h index b67636e71e28..fc325d79c03f 100644 --- a/js/src/jit/arm/MacroAssembler-arm-inl.h +++ b/js/src/jit/arm/MacroAssembler-arm-inl.h @@ -2301,6 +2301,8 @@ MacroAssembler::wasmBoundsCheck(Condition cond, Register index, Register boundsC { as_cmp(index, O2Reg(boundsCheckLimit)); as_b(label, cond); + if (JitOptions.spectreIndexMasking) + ma_mov(boundsCheckLimit, index, LeaveCC, cond); } template @@ -2312,6 +2314,8 @@ MacroAssembler::wasmBoundsCheck(Condition cond, Register index, Address boundsCh ma_ldr(DTRAddr(boundsCheckLimit.base, DtrOffImm(boundsCheckLimit.offset)), scratch); as_cmp(index, O2Reg(scratch)); as_b(label, cond); + if (JitOptions.spectreIndexMasking) + ma_mov(scratch, index, LeaveCC, cond); } //}}} check_macroassembler_style diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index bd33bd99bd63..92846a917466 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -8428,7 +8428,7 @@ class LWasmAddOffset : public LInstructionHelper<1, 1, 0> } }; -class LWasmBoundsCheck : public LInstructionHelper<0, 2, 0> +class LWasmBoundsCheck : public LInstructionHelper<1, 2, 0> { public: LIR_HEADER(WasmBoundsCheck); diff --git a/js/src/jit/x86/MacroAssembler-x86-inl.h b/js/src/jit/x86/MacroAssembler-x86-inl.h index 43e5df690faf..5206028a8c24 100644 --- a/js/src/jit/x86/MacroAssembler-x86-inl.h +++ b/js/src/jit/x86/MacroAssembler-x86-inl.h @@ -1109,6 +1109,8 @@ MacroAssembler::wasmBoundsCheck(Condition cond, Register index, Register boundsC { cmp32(index, boundsCheckLimit); j(cond, label); + if (JitOptions.spectreIndexMasking) + cmovCCl(cond, Operand(boundsCheckLimit), index); } template @@ -1117,6 +1119,8 @@ MacroAssembler::wasmBoundsCheck(Condition cond, Register index, Address boundsCh { cmp32(index, Operand(boundsCheckLimit)); j(cond, label); + if (JitOptions.spectreIndexMasking) + cmovCCl(cond, Operand(boundsCheckLimit), index); } //}}} check_macroassembler_style diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index daa8fb04062e..c6a7383585a7 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1683,11 +1683,11 @@ js::ReportInNotObjectError(JSContext* cx, HandleValue lref, int lindex, HandleValue rref, int rindex) { auto uniqueCharsFromString = [](JSContext* cx, HandleValue ref) -> UniqueChars { - static const size_t MAX_STRING_LENGTH = 16; + static const size_t MaxStringLength = 16; RootedString str(cx, ref.toString()); - if (str->length() > MAX_STRING_LENGTH) { + if (str->length() > MaxStringLength) { StringBuffer buf(cx); - if (!buf.appendSubstring(str, 0, MAX_STRING_LENGTH)) + if (!buf.appendSubstring(str, 0, MaxStringLength)) return nullptr; if (!buf.append("...")) return nullptr; diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index bcb633fdfb83..0d93d85053e6 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -42,6 +42,7 @@ #include "js/CharacterEncoding.h" #include "js/Date.h" #include "js/Wrapper.h" +#include "vm/ArgumentsObject.h" #include "vm/Compression.h" #include "vm/GeneratorObject.h" #include "vm/Interpreter.h" @@ -3194,3 +3195,9 @@ js::GetSelfHostedFunctionName(JSFunction* fun) static_assert(JSString::MAX_LENGTH <= INT32_MAX, "StringIteratorNext in builtin/String.js assumes the stored index " "into the string is an Int32Value"); + +static_assert(JSString::MAX_LENGTH == MAX_STRING_LENGTH, + "JSString::MAX_LENGTH matches self-hosted constant for maximum string length"); + +static_assert(ARGS_LENGTH_MAX == MAX_ARGS_LENGTH, + "ARGS_LENGTH_MAX matches self-hosted constant for maximum arguments length"); diff --git a/js/src/vm/StructuredClone.cpp b/js/src/vm/StructuredClone.cpp index c7ddb7801cf4..c7cfd1d34015 100644 --- a/js/src/vm/StructuredClone.cpp +++ b/js/src/vm/StructuredClone.cpp @@ -5,26 +5,25 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* - * This file implements the structured clone algorithm of - * http://www.whatwg.org/specs/web-apps/current-work/multipage/common-dom-interfaces.html#safe-passing-of-structured-data + * This file implements the structured data algorithms of + * https://html.spec.whatwg.org/multipage/structured-data.html * - * The implementation differs slightly in that it uses an explicit stack, and - * the "memory" maps source objects to sequential integer indexes rather than - * directly pointing to destination objects. As a result, the order in which - * things are added to the memory must exactly match the order in which they - * are placed into 'allObjs', an analogous array of back-referenceable - * destination objects constructed while reading. + * The spec is in two parts: * - * For the most part, this is easy: simply add objects to the memory when first - * encountering them. But reading in a typed array requires an ArrayBuffer for - * construction, so objects cannot just be added to 'allObjs' in the order they - * are created. If they were, ArrayBuffers would come before typed arrays when - * in fact the typed array was added to 'memory' first. + * - StructuredSerialize examines a JS value and produces a graph of Records. + * - StructuredDeserialize walks the Records and produces a new JS value. * - * So during writing, we add objects to the memory when first encountering - * them. When reading a typed array, a placeholder is pushed onto allObjs until - * the ArrayBuffer has been read, then it is updated with the actual typed - * array object. + * The differences between our implementation and the spec are minor: + * + * - We call the two phases "write" and "read". + * - Our algorithms use an explicit work stack, rather than recursion. + * - Serialized data is a flat array of bytes, not a (possibly cyclic) graph + * of "Records". + * - As a consequence, we handle non-treelike object graphs differently. + * We serialize objects that appear in multiple places in the input as + * backreferences, using sequential integer indexes. + * See `JSStructuredCloneReader::allObjs`, our take on the "memory" map + * in the spec's StructuredDeserialize. */ #include "js/StructuredClone.h" @@ -71,7 +70,7 @@ using JS::CanonicalizeNaN; // sizing data structures. enum StructuredDataType : uint32_t { - /* Structured data types provided by the engine */ + // Structured data types provided by the engine SCTAG_FLOAT_MAX = 0xFFF00000, SCTAG_HEADER = 0xFFF10000, SCTAG_NULL = 0xFFFF0000, @@ -119,11 +118,9 @@ enum StructuredDataType : uint32_t { SCTAG_TYPED_ARRAY_V1_UINT8_CLAMPED = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Uint8Clamped, SCTAG_TYPED_ARRAY_V1_MAX = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::MaxTypedArrayViewType - 1, - /* - * Define a separate range of numbers for Transferable-only tags, since - * they are not used for persistent clone buffers and therefore do not - * require bumping JS_STRUCTURED_CLONE_VERSION. - */ + // Define a separate range of numbers for Transferable-only tags, since + // they are not used for persistent clone buffers and therefore do not + // require bumping JS_STRUCTURED_CLONE_VERSION. SCTAG_TRANSFER_MAP_HEADER = 0xFFFF0200, SCTAG_TRANSFER_MAP_PENDING_ENTRY, SCTAG_TRANSFER_MAP_ARRAY_BUFFER, @@ -380,7 +377,7 @@ class SCInput { BufferIterator point; }; -} /* namespace js */ +} // namespace js struct JSStructuredCloneReader { public: @@ -432,7 +429,17 @@ struct JSStructuredCloneReader { // Stack of objects with properties remaining to be read. AutoValueVector objs; - // Stack of all objects read during this deserialization + // Array of all objects read during this deserialization, for resolving + // backreferences. + // + // For backreferences to work correctly, objects must be added to this + // array in exactly the order expected by the version of the Writer that + // created the serialized data, even across years and format versions. This + // is usually no problem, since both algorithms do a single linear pass + // over the serialized data. There is one hitch; see readTypedArray. + // + // The values in this vector are objects, except it can temporarily have + // one `undefined` placeholder value (the readTypedArray hack). AutoValueVector allObjs; // The user defined callbacks that will be used for cloning. @@ -736,7 +743,7 @@ bool SCInput::read(uint64_t* p) { if (!point.canPeek()) { - *p = 0; /* initialize to shut GCC up */ + *p = 0; // initialize to shut GCC up return reportTruncated(); } *p = NativeEndian::swapFromLittleEndian(point.peek()); @@ -748,7 +755,7 @@ bool SCInput::readNativeEndian(uint64_t* p) { if (!point.canPeek()) { - *p = 0; /* initialize to shut GCC up */ + *p = 0; // initialize to shut GCC up return reportTruncated(); } *p = point.peek(); @@ -844,9 +851,7 @@ SCInput::readArray(T* p, size_t nelems) JS_STATIC_ASSERT(sizeof(uint64_t) % sizeof(T) == 0); - /* - * Fail if nelems is so huge that computing the full size will overflow. - */ + // Fail if nelems is so huge that computing the full size will overflow. mozilla::CheckedInt size = mozilla::CheckedInt(nelems) * sizeof(T); if (!size.isValid()) return reportTruncated(); @@ -915,15 +920,13 @@ SCOutput::write(uint64_t u) bool SCOutput::writePair(uint32_t tag, uint32_t data) { - /* - * As it happens, the tag word appears after the data word in the output. - * This is because exponents occupy the last 2 bytes of doubles on the - * little-endian platforms we care most about. - * - * For example, TrueValue() is written using writePair(SCTAG_BOOLEAN, 1). - * PairToUInt64 produces the number 0xFFFF000200000001. - * That is written out as the bytes 01 00 00 00 02 00 FF FF. - */ + // As it happens, the tag word appears after the data word in the output. + // This is because exponents occupy the last 2 bytes of doubles on the + // little-endian platforms we care most about. + // + // For example, TrueValue() is written using writePair(SCTAG_BOOLEAN, 1). + // PairToUInt64 produces the number 0xFFFF000200000001. + // That is written out as the bytes 01 00 00 00 02 00 FF FF. return write(PairToUInt64(tag, data)); } @@ -1031,7 +1034,7 @@ SCOutput::discardTransferables(const JSStructuredCloneCallbacks* cb, void* cbClo DiscardTransferables(buf, cb, cbClosure); } -} /* namespace js */ +} // namespace js JSStructuredCloneData::~JSStructuredCloneData() { @@ -1152,7 +1155,7 @@ inline void JSStructuredCloneWriter::checkStack() { #ifdef DEBUG - /* To avoid making serialization O(n^2), limit stack-checking at 10. */ + // To avoid making serialization O(n^2), limit stack-checking at 10. const size_t MAX = 10; size_t limit = Min(counts.length(), MAX); @@ -1296,7 +1299,7 @@ JSStructuredCloneWriter::writeSharedWasmMemory(HandleObject obj) bool JSStructuredCloneWriter::startObject(HandleObject obj, bool* backref) { - /* Handle cycles in the object graph. */ + // Handle cycles in the object graph. CloneMemory::AddPtr p = memory.lookupForAdd(obj); if ((*backref = p.found())) return out.writePair(SCTAG_BACK_REFERENCE_OBJECT, p->value()); @@ -1317,10 +1320,8 @@ JSStructuredCloneWriter::startObject(HandleObject obj, bool* backref) bool JSStructuredCloneWriter::traverseObject(HandleObject obj) { - /* - * Get enumerable property ids and put them in reverse order so that they - * will come off the stack in forward order. - */ + // Get enumerable property ids and put them in reverse order so that they + // will come off the stack in forward order. AutoIdVector properties(context()); if (!GetPropertyKeys(context(), obj, JSITER_OWNONLY, &properties)) return false; @@ -1332,13 +1333,13 @@ JSStructuredCloneWriter::traverseObject(HandleObject obj) return false; } - /* Push obj and count to the stack. */ + // Push obj and count to the stack. if (!objs.append(ObjectValue(*obj)) || !counts.append(properties.length())) return false; checkStack(); - /* Write the header for obj. */ + // Write the header for obj. ESClass cls; if (!GetBuiltinClass(context(), obj, &cls)) return false; @@ -1365,13 +1366,13 @@ JSStructuredCloneWriter::traverseMap(HandleObject obj) return false; } - /* Push obj and count to the stack. */ + // Push obj and count to the stack. if (!objs.append(ObjectValue(*obj)) || !counts.append(newEntries.length())) return false; checkStack(); - /* Write the header for obj. */ + // Write the header for obj. return out.writePair(SCTAG_MAP_OBJECT, 0); } @@ -1395,13 +1396,13 @@ JSStructuredCloneWriter::traverseSet(HandleObject obj) return false; } - /* Push obj and count to the stack. */ + // Push obj and count to the stack. if (!objs.append(ObjectValue(*obj)) || !counts.append(keys.length())) return false; checkStack(); - /* Write the header for obj. */ + // Write the header for obj. return out.writePair(SCTAG_SET_OBJECT, 0); } @@ -1587,7 +1588,7 @@ JSStructuredCloneWriter::startWrite(HandleValue v) if (callbacks && callbacks->write) return callbacks->write(context(), this, obj, closure); - /* else fall through */ + // else fall through } return reportDataCloneError(JS_SCERR_UNSUPPORTED_TYPE); @@ -1786,11 +1787,9 @@ JSStructuredCloneWriter::write(HandleValue v) return false; MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id)); - /* - * If obj still has an own property named id, write it out. - * The cost of re-checking could be avoided by using - * NativeIterators. - */ + // If obj still has an own property named id, write it out. + // The cost of re-checking could be avoided by using + // NativeIterators. bool found; if (!HasOwnProperty(context(), obj, id, &found)) return false; @@ -1852,7 +1851,7 @@ class Chars { void forget() { p = nullptr; } }; -} /* anonymous namespace */ +} // anonymous namespace template JSString* @@ -1897,7 +1896,7 @@ JSStructuredCloneReader::readTypedArray(uint32_t arrayType, uint32_t nelems, Mut return false; } - // Push a placeholder onto the allObjs list to stand in for the typed array + // Push a placeholder onto the allObjs list to stand in for the typed array. uint32_t placeholderIndex = allObjs.length(); Value dummy = UndefinedValue(); if (!allObjs.append(dummy)) diff --git a/js/src/wasm/WasmCompile.cpp b/js/src/wasm/WasmCompile.cpp index 396af8b68a3c..6d957e7f2726 100644 --- a/js/src/wasm/WasmCompile.cpp +++ b/js/src/wasm/WasmCompile.cpp @@ -414,6 +414,8 @@ InitialCompileFlags(const CompileArgs& args, Decoder& d, CompileMode* mode, Tier *debug = debugEnabled ? DebugEnabled::True : DebugEnabled::False; } +extern unsigned redundantCount; + SharedModule wasm::CompileBuffer(const CompileArgs& args, const ShareableBytes& bytecode, UniqueChars* error) { @@ -441,7 +443,11 @@ wasm::CompileBuffer(const CompileArgs& args, const ShareableBytes& bytecode, Uni if (!DecodeModuleTail(d, &env)) return nullptr; - return mg.finishModule(bytecode); + auto module = mg.finishModule(bytecode); + + printf("# redundant: %u\n", redundantCount); + + return module; } bool diff --git a/js/src/wasm/WasmIonCompile.cpp b/js/src/wasm/WasmIonCompile.cpp index baf00251f0cf..13f34c5e3135 100644 --- a/js/src/wasm/WasmIonCompile.cpp +++ b/js/src/wasm/WasmIonCompile.cpp @@ -832,8 +832,12 @@ class FunctionCompiler } MWasmLoadTls* boundsCheckLimit = maybeLoadBoundsCheckLimit(); - if (boundsCheckLimit) - curBlock_->add(MWasmBoundsCheck::New(alloc(), *base, boundsCheckLimit, bytecodeOffset())); + if (boundsCheckLimit) { + auto* ins = MWasmBoundsCheck::New(alloc(), *base, boundsCheckLimit, bytecodeOffset()); + curBlock_->add(ins); + if (JitOptions.spectreIndexMasking) + *base = ins; + } } bool isSmallerAccessForI64(ValType result, const MemoryAccessDesc* access) { diff --git a/mobile/android/geckoview/build.gradle b/mobile/android/geckoview/build.gradle index 8e2662f97b9e..9f26fd85d7c4 100644 --- a/mobile/android/geckoview/build.gradle +++ b/mobile/android/geckoview/build.gradle @@ -184,7 +184,8 @@ android.libraryVariants.all { variant -> classpath = files(variant.javaCompile.classpath.files) source = files(variant.javaCompile.source) - exclude '**/R.java', '**/BuildConfig.java', 'com/google/**', 'org/webrtc/**' + exclude '**/R.java', '**/BuildConfig.java' + include 'org/mozilla/geckoview/**' options.addPathOption('sourcepath', ':').setValue( variant.sourceSets.collect({ it.javaDirectories }).flatten() + variant.generateBuildConfig.sourceOutputDir) @@ -195,6 +196,7 @@ android.libraryVariants.all { variant -> file("${System.properties['java.home']}/lib/rt.jar")] + android.bootClasspath options.memberLevel = JavadocMemberLevel.PROTECTED options.source = 7 + options.links("https://d.android.com/reference/") options.docTitle = "GeckoView ${mozconfig.substs.MOZ_APP_VERSION} API" options.header = "GeckoView ${mozconfig.substs.MOZ_APP_VERSION} API" diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java index 58c3608e0bb5..65d14071fcd3 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java @@ -353,7 +353,7 @@ public class GeckoSession extends LayerSession private final Listener mListener = new Listener(); - protected static final class Window extends JNIObject implements IInterface { + /* package */ static final class Window extends JNIObject implements IInterface { private NativeQueue mNativeQueue; private Binder mBinder; @@ -1405,17 +1405,6 @@ public class GeckoSession extends LayerSession } } - enum LoadUriResult { - HANDLED(0), - LOAD_IN_FRAME(1); - - private int mValue; - - private LoadUriResult(int value) { - mValue = value; - } - } - /** * A request to open an URI. * @param session The GeckoSession that initiated the callback. diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TextInputController.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TextInputController.java index 18c63f3729cd..7f95bb589c43 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TextInputController.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TextInputController.java @@ -107,7 +107,7 @@ public final class TextInputController { mEditable.setDefaultEditableChild(mEditableChild); } - public void onWindowChanged(final GeckoSession.Window window) { + /* package */ void onWindowChanged(final GeckoSession.Window window) { if (mQueue.isReady()) { window.attachEditable(mEditable, mEditableChild); } else { diff --git a/modules/libpref/Preferences.cpp b/modules/libpref/Preferences.cpp index 491aeec95733..487313481571 100644 --- a/modules/libpref/Preferences.cpp +++ b/modules/libpref/Preferences.cpp @@ -326,8 +326,9 @@ static ArenaAllocator<8192, 1> gPrefNameArena; class Pref { public: - explicit Pref() - : mType(static_cast(PrefType::None)) + explicit Pref(const char* aName) + : mName(ArenaStrdup(aName, gPrefNameArena)) + , mType(static_cast(PrefType::None)) , mIsSticky(false) , mIsLocked(false) , mHasDefaultValue(false) @@ -340,10 +341,15 @@ public: ~Pref() { + // There's no need to free mName because it's allocated in memory owned by + // gPrefNameArena. + mDefaultValue.Clear(Type()); mUserValue.Clear(Type()); } + const char* Name() { return mName; } + // Types. PrefType Type() const { return static_cast(mType); } @@ -395,6 +401,15 @@ public: // Other operations. + bool MatchEntry(const char* aPrefName) + { + if (!mName || !aPrefName) { + return false; + } + + return strcmp(mName, aPrefName) == 0; + } + nsresult GetBoolValue(PrefValueKind aKind, bool* aResult) { if (!IsTypeBool()) { @@ -454,16 +469,11 @@ public: return NS_OK; } - // Fills out a dom pref with the values from this pref. - // @param aDomPref The pref to update. - // @param aName Optional. Override the dom pref's name with aName. - void ToDomPref(dom::Pref* aDomPref, const char* aName = nullptr) + void ToDomPref(dom::Pref* aDomPref) { MOZ_ASSERT(XRE_IsParentProcess()); - if (aName) { - aDomPref->name() = aName; - } + aDomPref->name() = mName; aDomPref->isLocked() = mIsLocked; @@ -492,6 +502,7 @@ public: void FromDomPref(const dom::Pref& aDomPref, bool* aValueChanged) { MOZ_ASSERT(!XRE_IsParentProcess()); + MOZ_ASSERT(strcmp(mName, aDomPref.name().get()) == 0); mIsLocked = aDomPref.isLocked(); @@ -677,6 +688,7 @@ public: void AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, PrefsSizes& aSizes) { + // Note: mName is allocated in gPrefNameArena, measured elsewhere. aSizes.mPrefValues += aMallocSizeOf(this); if (IsTypeString()) { if (mHasDefaultValue) { @@ -689,6 +701,8 @@ public: } private: + const char* mName; // allocated in gPrefNameArena + uint32_t mType : 2; uint32_t mIsSticky : 1; uint32_t mIsLocked : 1; @@ -700,6 +714,36 @@ private: PrefValue mUserValue; }; +class PrefEntry : public PLDHashEntryHdr +{ +public: + Pref* mPref; // Note: this is never null in a live entry. + + static bool MatchEntry(const PLDHashEntryHdr* aEntry, const void* aKey) + { + auto entry = static_cast(aEntry); + auto prefName = static_cast(aKey); + + return entry->mPref->MatchEntry(prefName); + } + + static void InitEntry(PLDHashEntryHdr* aEntry, const void* aKey) + { + auto entry = static_cast(aEntry); + auto prefName = static_cast(aKey); + + entry->mPref = new Pref(prefName); + } + + static void ClearEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry) + { + auto entry = static_cast(aEntry); + + delete entry->mPref; + entry->mPref = nullptr; + } +}; + struct CallbackNode { CallbackNode(const char* aDomain, @@ -725,21 +769,7 @@ struct CallbackNode CallbackNode* mNext; }; -// Hash key that allocates its string from the `gPrefNameArena`. The base class -// `nsDepCharHashKey` does not own the string data and no copies are made. -class StringArenaHashKey : public nsDepCharHashKey -{ -public: - explicit StringArenaHashKey(const char* aKey) - : nsDepCharHashKey(ArenaStrdup(aKey, gPrefNameArena)) - { - } -}; - -// Hashtable used to map names to pref objects. StringArenaHashKey is used to -// allocate the strings out of an arena. -using PrefsTable = nsClassHashtable; -static PrefsTable* gHashTable; +static PLDHashTable* gHashTable; // The callback list contains all the priority callbacks followed by the // non-priority callbacks. gLastPriorityNode records where the first part ends. @@ -752,6 +782,14 @@ static bool gIsAnyPrefLocked = false; static bool gCallbacksInProgress = false; static bool gShouldCleanupDeadNodes = false; +static PLDHashTableOps pref_HashTableOps = { + PLDHashTable::HashStringKey, + PrefEntry::MatchEntry, + PLDHashTable::MoveEntryStub, + PrefEntry::ClearEntry, + PrefEntry::InitEntry, +}; + static Pref* pref_HashTableLookup(const char* aPrefName); @@ -765,10 +803,10 @@ pref_savePrefs() { MOZ_ASSERT(NS_IsMainThread()); - PrefSaveData savedPrefs(gHashTable->Count()); + PrefSaveData savedPrefs(gHashTable->EntryCount()); for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) { - Pref* pref = iter.Data(); + Pref* pref = static_cast(iter.Get())->mPref; nsAutoCString prefValueStr; if (!pref->UserValueToStringForSaving(prefValueStr)) { @@ -776,7 +814,7 @@ pref_savePrefs() } nsAutoCString prefNameStr; - StrEscape(iter.Key(), prefNameStr); + StrEscape(pref->Name(), prefNameStr); nsPrintfCString str( "user_pref(%s, %s);", prefNameStr.get(), prefValueStr.get()); @@ -827,7 +865,7 @@ IsEarlyPref(const char* aPrefName) #endif // DEBUG -static PrefsTable::LookupResult +static PrefEntry* pref_HashTableLookupInner(const char* aPrefName) { MOZ_ASSERT(NS_IsMainThread() || mozilla::ServoStyleSet::IsInServoTraversal()); @@ -850,14 +888,14 @@ pref_HashTableLookupInner(const char* aPrefName) } #endif - return gHashTable->Lookup(aPrefName); + return static_cast(gHashTable->Search(aPrefName)); } static Pref* pref_HashTableLookup(const char* aPrefName) { - PrefsTable::LookupResult entry = pref_HashTableLookupInner(aPrefName); - return entry ? static_cast(entry.Data()) : nullptr; + PrefEntry* entry = pref_HashTableLookupInner(aPrefName); + return entry ? entry->mPref : nullptr; } static nsresult @@ -874,7 +912,12 @@ pref_SetPref(const char* aPrefName, return NS_ERROR_OUT_OF_MEMORY; } - Pref* pref = gHashTable->LookupOrAdd(aPrefName); + auto entry = static_cast(gHashTable->Add(aPrefName, fallible)); + if (!entry) { + return NS_ERROR_OUT_OF_MEMORY; + } + + Pref* pref = entry->mPref; if (pref->IsTypeNone()) { // New entry. Set the type. pref->SetType(aType); @@ -1987,11 +2030,13 @@ nsPrefBranch::DeleteBranch(const char* aStartingAt) Substring(branchName, 0, branchName.Length() - 1); for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) { + Pref* pref = static_cast(iter.Get())->mPref; + // The first disjunct matches branches: e.g. a branch name "foo.bar." // matches a name "foo.bar.baz" (but it won't match "foo.barrel.baz"). // The second disjunct matches leaf nodes: e.g. a branch name "foo.bar." // matches a name "foo.bar" (by ignoring the trailing '.'). - nsDependentCString name(iter.Key()); + nsDependentCString name(pref->Name()); if (StringBeginsWith(name, branchName) || name.Equals(branchNameNoDot)) { iter.Remove(); } @@ -2026,8 +2071,9 @@ nsPrefBranch::GetChildList(const char* aStartingAt, const PrefName& parent = GetPrefName(aStartingAt); size_t parentLen = parent.Length(); for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) { - if (strncmp(iter.Key(), parent.get(), parentLen) == 0) { - prefArray.AppendElement(iter.Key()); + Pref* pref = static_cast(iter.Get())->mPref; + if (strncmp(pref->Name(), parent.get(), parentLen) == 0) { + prefArray.AppendElement(pref->Name()); } } @@ -2639,7 +2685,7 @@ PreferenceServiceReporter::CollectReports( if (gHashTable) { sizes.mHashTable += gHashTable->ShallowSizeOfIncludingThis(mallocSizeOf); for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) { - Pref* pref = iter.Data(); + Pref* pref = static_cast(iter.Get())->mPref; pref->AddSizeOfIncludingThis(mallocSizeOf, sizes); } } @@ -2832,7 +2878,8 @@ Preferences::GetInstanceForService() sPreferences = new Preferences(); MOZ_ASSERT(!gHashTable); - gHashTable = new PrefsTable(PREF_HASHTABLE_INITIAL_LENGTH); + gHashTable = new PLDHashTable( + &pref_HashTableOps, sizeof(PrefEntry), PREF_HASHTABLE_INITIAL_LENGTH); gTelemetryLoadData = new nsDataHashtable(); @@ -3087,7 +3134,7 @@ Preferences::ResetPrefs() { ENSURE_PARENT_PROCESS("Preferences::ResetPrefs", "all prefs"); - gHashTable->Clear(); + gHashTable->ClearAndPrepareForLength(PREF_HASHTABLE_INITIAL_LENGTH); gPrefNameArena.Clear(); return InitInitialObjects().isOk() ? NS_OK : NS_ERROR_FAILURE; @@ -3102,10 +3149,10 @@ Preferences::ResetUserPrefs() Vector prefNames; for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) { - Pref* pref = iter.Data(); + Pref* pref = static_cast(iter.Get())->mPref; if (pref->HasUserValue()) { - if (!prefNames.append(iter.Key())) { + if (!prefNames.append(pref->Name())) { return NS_ERROR_OUT_OF_MEMORY; } @@ -3179,7 +3226,12 @@ Preferences::SetPreference(const dom::Pref& aDomPref) const char* prefName = aDomPref.name().get(); - Pref* pref = gHashTable->LookupOrAdd(prefName); + auto entry = static_cast(gHashTable->Add(prefName, fallible)); + if (!entry) { + return; + } + + Pref* pref = entry->mPref; bool valueChanged = false; pref->FromDomPref(aDomPref, &valueChanged); @@ -3195,7 +3247,7 @@ Preferences::SetPreference(const dom::Pref& aDomPref) // needlessly, but that's ok because this case is rare. // if (!pref->HasDefaultValue() && !pref->HasUserValue()) { - gHashTable->Remove(prefName); + gHashTable->RemoveEntry(entry); } // Note: we don't have to worry about HandleDirty() because we are setting @@ -3223,9 +3275,9 @@ Preferences::GetPreferences(InfallibleTArray* aDomPrefs) MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); - aDomPrefs->SetCapacity(gHashTable->Count()); + aDomPrefs->SetCapacity(gHashTable->EntryCount()); for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) { - Pref* pref = iter.Data(); + Pref* pref = static_cast(iter.Get())->mPref; if (!pref->MustSendToContentProcesses()) { // The pref value hasn't changed since it was initialized at startup. @@ -3236,7 +3288,7 @@ Preferences::GetPreferences(InfallibleTArray* aDomPrefs) if (pref->HasAdvisablySizedValues()) { dom::Pref* setting = aDomPrefs->AppendElement(); - pref->ToDomPref(setting, iter.Key()); + pref->ToDomPref(setting); } } } @@ -4132,13 +4184,13 @@ Preferences::ClearUserInAnyProcess(const char* aPrefName) { NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE); - PrefsTable::LookupResult entry = pref_HashTableLookupInner(aPrefName); + PrefEntry* entry = pref_HashTableLookupInner(aPrefName); Pref* pref; - if (entry && (pref = entry.Data()) && pref->HasUserValue()) { + if (entry && (pref = entry->mPref) && pref->HasUserValue()) { pref->ClearUserValue(); if (!pref->HasDefaultValue()) { - entry.Remove(); + gHashTable->RemoveEntry(entry); } NotifyCallbacks(aPrefName); diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 7199aa8f0a91..d003cb0c4bd4 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -6012,3 +6012,8 @@ pref("layers.omtp.paint-workers", 1); #endif pref("layers.omtp.release-capture-on-main-thread", false); pref("layers.omtp.dump-capture", false); + +// Limits the depth of recursive conversion of data when opening +// a content to view. This is mostly intended to prevent infinite +// loops with faulty converters involved. +pref("general.document_open_conversion_depth_limit", 20); diff --git a/taskcluster/ci/beetmover-cdns/kind.yml b/taskcluster/ci/beetmover-cdns/kind.yml index 218bb84f61a3..a3a5e0d07f05 100644 --- a/taskcluster/ci/beetmover-cdns/kind.yml +++ b/taskcluster/ci/beetmover-cdns/kind.yml @@ -12,6 +12,7 @@ transforms: kind-dependencies: - release-generate-checksums + - release-update-verify job-defaults: worker-type: diff --git a/taskcluster/ci/build/linux.yml b/taskcluster/ci/build/linux.yml index e7f341ffb64d..cbb238ee7f41 100644 --- a/taskcluster/ci/build/linux.yml +++ b/taskcluster/ci/build/linux.yml @@ -803,3 +803,65 @@ linux64-add-on-devel/opt: - linux64-gcc - linux64-rust - linux64-sccache + +linux64-stylo-only/opt: + description: "Linux64 Stylo Only Opt" + index: + product: firefox + job-name: linux64-stylo-only-opt + treeherder: + platform: linux64-stylo-only/opt + symbol: B + tier: 2 + worker-type: aws-provisioner-v1/gecko-{level}-b-linux + worker: + max-run-time: 36000 + run: + using: mozharness + actions: [get-secrets build check-test update] + config: + - builds/releng_base_firefox.py + - builds/releng_base_linux_64_builds.py + script: "mozharness/scripts/fx_desktop_build.py" + extra-config: + mozconfig_variant: 'stylo-only' + secrets: true + tooltool-downloads: public + need-xvfb: true + run-on-projects: ['mozilla-central', 'try', 'mozilla-inbound', 'autoland'] + toolchains: + - linux64-clang + - linux64-gcc + - linux64-rust + - linux64-sccache + +linux64-stylo-only/debug: + description: "Linux64 Stylo Only Debug" + index: + product: firefox + job-name: linux64-stylo-only-debug + treeherder: + platform: linux64-stylo-only/debug + symbol: B + tier: 2 + worker-type: aws-provisioner-v1/gecko-{level}-b-linux + worker: + max-run-time: 36000 + run: + using: mozharness + actions: [get-secrets build check-test update] + config: + - builds/releng_base_firefox.py + - builds/releng_base_linux_64_builds.py + script: "mozharness/scripts/fx_desktop_build.py" + extra-config: + mozconfig_variant: 'stylo-only-debug' + secrets: true + tooltool-downloads: public + need-xvfb: true + run-on-projects: ['mozilla-central', 'try', 'mozilla-inbound', 'autoland'] + toolchains: + - linux64-clang + - linux64-gcc + - linux64-rust + - linux64-sccache diff --git a/taskcluster/taskgraph/transforms/tests.py b/taskcluster/taskgraph/transforms/tests.py index a66c38592377..c131fd405672 100644 --- a/taskcluster/taskgraph/transforms/tests.py +++ b/taskcluster/taskgraph/transforms/tests.py @@ -1018,8 +1018,8 @@ def make_job_description(config, tests): if test.get('when'): jobdesc['when'] = test['when'] - elif config.params['project'] != 'try': - # for non-try branches, include SETA + elif config.params['project'] != 'try' and suite not in INCLUSIVE_COMPONENTS: + # for non-try branches and non-inclusive suites, include SETA jobdesc['optimization'] = {'skip-unless-schedules-or-seta': schedules} else: # otherwise just use skip-unless-schedules diff --git a/testing/gtest/mozilla/MozGTestBench.h b/testing/gtest/mozilla/MozGTestBench.h index 082a61ad06ac..d7d98c489ab2 100644 --- a/testing/gtest/mozilla/MozGTestBench.h +++ b/testing/gtest/mozilla/MozGTestBench.h @@ -19,4 +19,9 @@ TEST(suite, test) { \ mozilla::GTestBench(#suite, #test, lambdaOrFunc); \ } +#define MOZ_GTEST_BENCH_F(suite, test, lambdaOrFunc) \ +TEST_F(suite, test) { \ + mozilla::GTestBench(#suite, #test, lambdaOrFunc); \ +} + #endif // GTEST_MOZGTESTBENCH_H diff --git a/testing/marionette/doc/PythonTests.md b/testing/marionette/doc/PythonTests.md index 9c6b8dbc9407..f52ea5531b8e 100644 --- a/testing/marionette/doc/PythonTests.md +++ b/testing/marionette/doc/PythonTests.md @@ -7,7 +7,7 @@ automating user interface tests. The in-tree test framework supports tests written in Python, using Python’s [unittest] library. Test cases are written as a subclass -of [`MarionetteTestCase`], with child tests belonging to instance +of `MarionetteTestCase`, with child tests belonging to instance methods that have a name starting with `test_`. You can additionally define [`setUp`] and [`tearDown`] instance @@ -36,9 +36,8 @@ The test structure is illustrated here: # code to execute after all tests are run MarionetteTestCase.tearDown(self) -[remote protocol]: https://developer.mozilla.org/en-US/docs/Mozilla/QA/Marionette/Protocol +[remote protocol]: Protocol.html [unittest]: https://docs.python.org/2.7/library/unittest.html -[`MarionetteTestCase`]: https://developer.mozilla.org/en-US/docs/Mozilla/QA/Marionette/MarionetteTestCase [`setUp`]: https://docs.python.org/2.7/library/unittest.html#unittest.TestCase.setUp [`setUpClass`]: https://docs.python.org/2.7/library/unittest.html#unittest.TestCase.setUpClass [`tearDown`]: https://docs.python.org/2.7/library/unittest.html#unittest.TestCase.tearDown @@ -62,10 +61,9 @@ The API ------- The full API documentation is found at -http://marionette-client.readthedocs.io/en/master/, but the key -objects are: +, but the key objects are: - * [`MarionetteTestCase`]: a subclass for `unittest.TestCase` + * `MarionetteTestCase`: a subclass for `unittest.TestCase` used as a base class for all tests to run. * [`Marionette`]: client that speaks to Firefox. diff --git a/testing/marionette/doc/SeleniumAtoms.md b/testing/marionette/doc/SeleniumAtoms.md index dc3b1c867b9d..9e73d079ceaa 100644 --- a/testing/marionette/doc/SeleniumAtoms.md +++ b/testing/marionette/doc/SeleniumAtoms.md @@ -1,75 +1,84 @@ -# Selenium Atoms +Selenium atoms +============== -Marionette uses a small list of [Selenium atoms] to interact with web elements. -Initially those have been added to ensure a better reliablity due to a wider usage -inside the Selenium project. But by adding full support for the [WebDriver -specification] they will be removed step by step. +Marionette uses a small list of [Selenium atoms] to interact with +web elements. Initially those have been added to ensure a better +reliablity due to a wider usage inside the Selenium project. But +by adding full support for the [WebDriver specification] they will +be removed step by step. Currently the following atoms are in use: -* clearElement -* getText -* isDisplayed -* isEnabled -* isSelected +- `getElementText` +- `isDisplayed` -To use one of those atoms Javascript modules will have to import [atom.js]. +To use one of those atoms Javascript modules will have to import +[atom.js]. [Selenium atoms]: https://github.com/SeleniumHQ/selenium/tree/master/javascript/webdriver/atoms [WebDriver specification]: https://w3c.github.io/webdriver/webdriver-spec.html -[atom.js]: ../atom.js +[atom.js]: https://searchfox.org/mozilla-central/source/testing/marionette/atom.js -## Update required Selenium Atoms +Update required Selenium atoms +------------------------------ + +In regular intervals the atoms, which are still in use, have to +be updated. Therefore they have to be exported from the Selenium +repository first, and then updated in [atom.js]. -In regular intervals the atoms, which are still in use, have to be updated. -Therefore they have to be exported from the Selenium repository first, and then -updated in [atom.js]. ### Export Selenium Atoms The canonical GitHub repository for Selenium is - https://github.com/SeleniumHQ/selenium.git + https://github.com/SeleniumHQ/selenium.git -so make sure to have a local copy of it. For the cloning process it is -recommended to specify the `--depth=1` argument, so only the last changeset is -getting downloaded (which itself will already be more than 100 MB). Once the -clone is ready the export of the atoms can be triggered by running the following -commands: +so make sure to have a local copy of it. For the cloning process +it is recommended to specify the `--depth=1` argument, so only the +last changeset is getting downloaded (which itself will already be +more than 100 MB). Once the clone is ready the export of the atoms +can be triggered by running the following commands: - % cd selenium - % ./go - % python buck-out/crazy-fun/%changeset%/buck.pex build --show-output %atom% + % cd selenium + % ./go + % python buck-out/crazy-fun/%changeset%/buck.pex build --show-output %atom% -Hereby `%changeset%` corresponds to the currently used version of buck, and -`%atom%` to the atom to export. The following targets for exporting are available: +Hereby `%changeset%` corresponds to the currently used version of +buck, and `%atom%` to the atom to export. The following targets +for exporting are available: -* //javascript/webdriver/atoms:clear-element-firefox -* //javascript/webdriver/atoms:get-text-firefox -* //javascript/webdriver/atoms:is-displayed-firefox -* //javascript/webdriver/atoms:is-enabled-firefox -* //javascript/webdriver/atoms:is-selected-firefox + - `//javascript/webdriver/atoms:clear-element-firefox` + - `//javascript/webdriver/atoms:get-text-firefox` + - `//javascript/webdriver/atoms:is-displayed-firefox` + - `//javascript/webdriver/atoms:is-enabled-firefox` + - `//javascript/webdriver/atoms:is-selected-firefox` For each of the exported atoms a file can now be found in the folder -`buck-out/gen/javascript/webdriver/atoms/`. They contain all the code including -dependencies for the atom wrapped into a single function. +`buck-out/gen/javascript/webdriver/atoms/`. They contain all the +code including dependencies for the atom wrapped into a single function. + ### Update atom.js To update the atoms for Marionette the `atoms.js` file has to be edited. For each atom to be updated the steps as layed out below have to be performed: -1. Open the Javascript file of the exported atom. See above for its location. -2. Remove the contained license header, which can be found somewhere in the - middle of the file. -3. Update the parameters of the wrapper function (at the very top) so that those - are equal with the used parameters in `atom.js`. -4. Copy the whole content of the file, and replace the existing code for the atom - in `atom.js`. +1. Open the Javascript file of the exported atom. See above for + its location. + +2. Remove the contained license header, which can be found somewhere + in the middle of the file. + +3. Update the parameters of the wrapper function (at the very top) + so that those are equal with the used parameters in `atom.js`. + +4. Copy the whole content of the file, and replace the existing + code for the atom in `atom.js`. + ### Test the changes -To ensure that the update of the atoms doesn't cause a regression a try build -should be run including Marionette unit tests, Firefox ui tests, and all the -web-platform-tests. +To ensure that the update of the atoms doesn't cause a regression +a try build should be run including Marionette unit tests, Firefox +ui tests, and all the web-platform-tests. diff --git a/testing/marionette/doc/index.rst b/testing/marionette/doc/index.rst index 6a60b92de814..0b8ffb18aa05 100644 --- a/testing/marionette/doc/index.rst +++ b/testing/marionette/doc/index.rst @@ -5,11 +5,6 @@ Marionette Marionette is the remote protocol that lets OOP programs communicate with, instrument, and control Gecko. - -Description -=========== - -Marionette is an automation driver for Mozilla’s Gecko engine. It can remotely control either the UI or the internal JavaScript of Gecko-based browsers, such as Firefox and Fennec. It can control both the chrome and the content document, giving a high level of diff --git a/testing/mozbase/mozlog/mozlog/commandline.py b/testing/mozbase/mozlog/mozlog/commandline.py index 64e25f22c798..456692ec794c 100644 --- a/testing/mozbase/mozlog/mozlog/commandline.py +++ b/testing/mozbase/mozlog/mozlog/commandline.py @@ -15,12 +15,18 @@ from . import formatters from .structuredlog import StructuredLogger, set_default_logger log_formatters = { - 'raw': (formatters.JSONFormatter, "Raw structured log messages"), - 'unittest': (formatters.UnittestFormatter, "Unittest style output"), - 'xunit': (formatters.XUnitFormatter, "xUnit compatible XML"), - 'html': (formatters.HTMLFormatter, "HTML report"), - 'mach': (formatters.MachFormatter, "Human-readable output"), - 'tbpl': (formatters.TbplFormatter, "TBPL style log format"), + 'raw': (formatters.JSONFormatter, "Raw structured log messages " + "(provided by mozlog)"), + 'unittest': (formatters.UnittestFormatter, "Unittest style output " + "(provided by mozlog)"), + 'xunit': (formatters.XUnitFormatter, "xUnit compatible XML " + "(povided by mozlog)"), + 'html': (formatters.HTMLFormatter, "HTML report " + "(provided by mozlog)"), + 'mach': (formatters.MachFormatter, "Human-readable output " + "(provided by mozlog)"), + 'tbpl': (formatters.TbplFormatter, "TBPL style log format " + "(provided by mozlog)"), 'errorsummary': (formatters.ErrorSummaryFormatter, argparse.SUPPRESS), } @@ -28,6 +34,9 @@ TEXT_FORMATTERS = ('raw', 'mach') """a subset of formatters for non test harnesses related applications""" +DOCS_URL = "https://firefox-source-docs.mozilla.org/mozbase/mozlog.html" + + def level_filter_wrapper(formatter, level): return handlers.LogLevelFilter(formatter, level) @@ -121,7 +130,9 @@ def add_logging_group(parser, include_formatters=None): group_name = "Output Logging" group_description = ("Each option represents a possible logging format " "and takes a filename to write that format to, " - "or '-' to write to stdout.") + "or '-' to write to stdout. Some options are " + "provided by the mozlog utility; see %s " + "for extended documentation." % DOCS_URL) if include_formatters is None: include_formatters = list(log_formatters.keys()) diff --git a/testing/talos/talos/tests/layout/benchmarks/displaylist_inactive_mutate.html b/testing/talos/talos/tests/layout/benchmarks/displaylist_inactive_mutate.html new file mode 100644 index 000000000000..dd74a2617dc3 --- /dev/null +++ b/testing/talos/talos/tests/layout/benchmarks/displaylist_inactive_mutate.html @@ -0,0 +1,56 @@ + + + + + + + + + diff --git a/testing/talos/talos/tests/layout/benchmarks/displaylist_mutate.html b/testing/talos/talos/tests/layout/benchmarks/displaylist_mutate.html index b1668e402db6..22120c4bd8a2 100644 --- a/testing/talos/talos/tests/layout/benchmarks/displaylist_mutate.html +++ b/testing/talos/talos/tests/layout/benchmarks/displaylist_mutate.html @@ -37,9 +37,6 @@ function runFrame() { if (window.tpRecordTime) { window.tpRecordTime(end - start, start); } - if (parent.reportResults) { - parent.reportResults(end - start, start); - } return; } diff --git a/testing/talos/talos/tests/layout/displaylist_mutate.manifest b/testing/talos/talos/tests/layout/displaylist_mutate.manifest index 0e09c83d3d40..277ab4f259e6 100644 --- a/testing/talos/talos/tests/layout/displaylist_mutate.manifest +++ b/testing/talos/talos/tests/layout/displaylist_mutate.manifest @@ -1 +1,2 @@ % http://localhost/tests/layout/benchmarks/displaylist_mutate.html +% http://localhost/tests/layout/benchmarks/displaylist_inactive_mutate.html diff --git a/toolkit/modules/RemoteFinder.jsm b/toolkit/modules/RemoteFinder.jsm index b6c5902f96cf..8c107bf1d640 100644 --- a/toolkit/modules/RemoteFinder.jsm +++ b/toolkit/modules/RemoteFinder.jsm @@ -209,10 +209,6 @@ function RemoteFinderListener(global) { this._finder = new Finder(global.docShell); this._finder.addResultListener(this); this._global = global; - this.KeyboardEvent = - global.docShell - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindow).KeyboardEvent; for (let msg of this.MESSAGES) { global.addMessageListener(msg, this); @@ -318,7 +314,8 @@ RemoteFinderListener.prototype = { break; case "Finder:KeyPress": - this._finder.keyPress(new this.KeyboardEvent("keypress", data)); + var KeyboardEvent = this._finder._getWindow().KeyboardEvent; + this._finder.keyPress(new KeyboardEvent("keypress", data)); break; case "Finder:MatchesCount": diff --git a/uriloader/base/nsURILoader.cpp b/uriloader/base/nsURILoader.cpp index 90d56dee15c0..ff0d8079c099 100644 --- a/uriloader/base/nsURILoader.cpp +++ b/uriloader/base/nsURILoader.cpp @@ -49,6 +49,7 @@ #include "mozilla/Attributes.h" #include "mozilla/IntegerPrintfMacros.h" #include "mozilla/Preferences.h" +#include "mozilla/Unused.h" #include "nsContentUtils.h" mozilla::LazyLogModule nsURILoader::mLog("URILoader"); @@ -60,6 +61,17 @@ mozilla::LazyLogModule nsURILoader::mLog("URILoader"); #define NS_PREF_DISABLE_BACKGROUND_HANDLING \ "security.exthelperapp.disable_background_handling" +static uint32_t sConvertDataLimit = 20; + +static bool InitPreferences() +{ + nsresult rv = mozilla::Preferences::AddUintVarCache( + &sConvertDataLimit, + "general.document_open_conversion_depth_limit", + 20); + return NS_SUCCEEDED(rv); +} + /** * The nsDocumentOpenInfo contains the state required when a single * document is being opened in order to discover the content type... @@ -157,6 +169,11 @@ protected: * nsIURIContentListeners. */ RefPtr mURILoader; + + /** + * Limit of data conversion depth to prevent infinite conversion loops + */ + uint32_t mDataConversionDepthLimit; }; NS_IMPL_ADDREF(nsDocumentOpenInfo) @@ -179,7 +196,8 @@ nsDocumentOpenInfo::nsDocumentOpenInfo(nsIInterfaceRequestor* aWindowContext, nsURILoader* aURILoader) : m_originalContext(aWindowContext), mFlags(aFlags), - mURILoader(aURILoader) + mURILoader(aURILoader), + mDataConversionDepthLimit(sConvertDataLimit) { } @@ -625,6 +643,12 @@ nsDocumentOpenInfo::ConvertData(nsIRequest *request, PromiseFlatCString(aSrcContentType).get(), PromiseFlatCString(aOutContentType).get())); + if (mDataConversionDepthLimit == 0) { + LOG(("[0x%p] nsDocumentOpenInfo::ConvertData - reached the recursion limit!", this)); + // This will fall back to external helper app handling. + return NS_ERROR_ABORT; + } + NS_PRECONDITION(aSrcContentType != aOutContentType, "ConvertData called when the two types are the same!"); nsresult rv = NS_OK; @@ -648,6 +672,9 @@ nsDocumentOpenInfo::ConvertData(nsIRequest *request, LOG((" Downstream DocumentOpenInfo would be: 0x%p", nextLink.get())); + // Decrease the conversion recursion limit by one to prevent infinite loops. + nextLink->mDataConversionDepthLimit = mDataConversionDepthLimit - 1; + // Make sure nextLink starts with the contentListener that said it wanted the // results of this decode. nextLink->m_contentListener = aListener; @@ -885,6 +912,9 @@ nsresult nsURILoader::OpenChannel(nsIChannel* channel, } } + static bool once = InitPreferences(); + mozilla::Unused << once; + // we need to create a DocumentOpenInfo object which will go ahead and open // the url and discover the content type.... RefPtr loader = diff --git a/xpcom/ds/nsISerializable.idl b/xpcom/ds/nsISerializable.idl index f525f3a8f3d1..1920fa1b16d4 100644 --- a/xpcom/ds/nsISerializable.idl +++ b/xpcom/ds/nsISerializable.idl @@ -17,7 +17,7 @@ interface nsISerializable : nsISupports * can't be set to default values must have been serialized by write, * and should be read from aInputStream in the same order by this method. */ - void read(in nsIObjectInputStream aInputStream); + [must_use] void read(in nsIObjectInputStream aInputStream); /** * Serialize the object implementing nsISerializable to aOutputStream, by diff --git a/xpcom/io/nsLocalFileWin.cpp b/xpcom/io/nsLocalFileWin.cpp index a2e8e366b55c..3b62e4835ea3 100644 --- a/xpcom/io/nsLocalFileWin.cpp +++ b/xpcom/io/nsLocalFileWin.cpp @@ -1188,7 +1188,7 @@ nsLocalFile::OpenNSPRFileDesc(int32_t aFlags, int32_t aMode, NS_IMETHODIMP nsLocalFile::OpenANSIFileDesc(const char* aMode, FILE** aResult) { - nsresult rv = ResolveAndStat(); + nsresult rv = Resolve(); if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) { return rv; } @@ -1210,7 +1210,7 @@ nsLocalFile::Create(uint32_t aType, uint32_t aAttributes) return NS_ERROR_FILE_UNKNOWN_TYPE; } - nsresult rv = ResolveAndStat(); + nsresult rv = Resolve(); if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) { return rv; } @@ -1625,7 +1625,7 @@ typedef struct NS_IMETHODIMP nsLocalFile::GetVersionInfoField(const char* aField, nsAString& aResult) { - nsresult rv = ResolveAndStat(); + nsresult rv = Resolve(); if (NS_FAILED(rv)) { return rv; } @@ -2312,7 +2312,7 @@ nsLocalFile::SetLastModifiedTime(PRTime aLastModifiedTime) // Check we are correctly initialized. CHECK_mWorkingPath(); - nsresult rv = ResolveAndStat(); + nsresult rv = Resolve(); if (NS_FAILED(rv)) { return rv; } @@ -2458,7 +2458,7 @@ nsLocalFile::SetPermissions(uint32_t aPermissions) // If true, then this will be for the target of the shortcut file, // otherwise it will be for the shortcut file itself (i.e. the same // results as SetPermissionsOfLink) - nsresult rv = ResolveAndStat(); + nsresult rv = Resolve(); if (NS_FAILED(rv)) { return rv; } @@ -2547,7 +2547,7 @@ nsLocalFile::SetFileSize(int64_t aFileSize) // Check we are correctly initialized. CHECK_mWorkingPath(); - nsresult rv = ResolveAndStat(); + nsresult rv = Resolve(); if (NS_FAILED(rv)) { return rv; } @@ -3051,7 +3051,7 @@ nsLocalFile::GetTarget(nsAString& aResult) return NS_ERROR_FILE_INVALID_PATH; } #endif - ResolveAndStat(); + Resolve(); aResult = mResolvedPath; return NS_OK;