diff --git a/browser/components/contextualidentity/test/browser/browser_eme.js b/browser/components/contextualidentity/test/browser/browser_eme.js index 25e4851d08d2..dd8cdd3f7339 100644 --- a/browser/components/contextualidentity/test/browser/browser_eme.js +++ b/browser/components/contextualidentity/test/browser/browser_eme.js @@ -103,7 +103,7 @@ add_task(async function test() { let access = await content.navigator.requestMediaKeySystemAccess("org.w3.clearkey", [{ initDataTypes: [aKeyInfo.initDataType], - videoCapabilities: [{contentType: 'video/webm; codecs="vp9"'}], + videoCapabilities: [{contentType: "video/webm"}], sessionTypes: ["persistent-license"], persistentState: "required", }]); @@ -156,7 +156,7 @@ add_task(async function test() { let access = await content.navigator.requestMediaKeySystemAccess("org.w3.clearkey", [{ initDataTypes: [aKeyInfo.initDataType], - videoCapabilities: [{contentType: 'video/webm; codecs="vp9"'}], + videoCapabilities: [{contentType: "video/webm"}], sessionTypes: ["persistent-license"], persistentState: "required", }]); diff --git a/browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js b/browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js index 356fe0332eda..b6e948284a12 100644 --- a/browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js +++ b/browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js @@ -98,7 +98,7 @@ async function setupEMEKey(browser) { let access = await content.navigator.requestMediaKeySystemAccess("org.w3.clearkey", [{ initDataTypes: [aKeyInfo.initDataType], - videoCapabilities: [{contentType: 'video/webm; codecs="vp9"'}], + videoCapabilities: [{contentType: "video/webm"}], sessionTypes: ["persistent-license"], persistentState: "required", }]); @@ -153,7 +153,7 @@ async function checkEMEKey(browser, emeSessionId) { let access = await content.navigator.requestMediaKeySystemAccess("org.w3.clearkey", [{ initDataTypes: [aKeyInfo.initDataType], - videoCapabilities: [{contentType: 'video/webm; codecs="vp9"'}], + videoCapabilities: [{contentType: "video/webm"}], sessionTypes: ["persistent-license"], persistentState: "required", }]); diff --git a/browser/components/extensions/moz.build b/browser/components/extensions/moz.build index 62638aa3560f..0dfbc28ec7c9 100644 --- a/browser/components/extensions/moz.build +++ b/browser/components/extensions/moz.build @@ -27,4 +27,12 @@ BROWSER_CHROME_MANIFESTS += [ ] MOCHITEST_MANIFESTS += ['test/mochitest/mochitest.ini'] -XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell/xpcshell.ini'] + +if CONFIG['OS_ARCH'] != 'Darwin': + XPCSHELL_TESTS_MANIFESTS += [ + 'test/xpcshell/xpcshell-remote.ini', + ] + +XPCSHELL_TESTS_MANIFESTS += [ + 'test/xpcshell/xpcshell.ini', +] diff --git a/browser/components/extensions/test/browser/browser-common.ini b/browser/components/extensions/test/browser/browser-common.ini index 65f6db760aab..ff2cbbba54d1 100644 --- a/browser/components/extensions/test/browser/browser-common.ini +++ b/browser/components/extensions/test/browser/browser-common.ini @@ -37,6 +37,7 @@ support-files = [browser_ext_browserAction_pageAction_icon_permissions.js] [browser_ext_browserAction_popup.js] [browser_ext_browserAction_popup_preload.js] +skip-if = (os == 'win' && !debug) # bug 1352668 [browser_ext_browserAction_popup_resize.js] [browser_ext_browserAction_simple.js] [browser_ext_browsingData_formData.js] diff --git a/browser/components/extensions/test/browser/head.js b/browser/components/extensions/test/browser/head.js index 6d15b5a409a3..67605e3c3a5b 100644 --- a/browser/components/extensions/test/browser/head.js +++ b/browser/components/extensions/test/browser/head.js @@ -41,6 +41,7 @@ const {CustomizableUI} = Cu.import("resource:///modules/CustomizableUI.jsm", {}) if (gTestPath.includes("test-oop-extensions")) { SpecialPowers.pushPrefEnv({set: [ ["extensions.webextensions.remote", true], + ["layers.popups.compositing.enabled", true], ]}); // We don't want to reset this at the end of the test, so that we don't have // to spawn a new extension child process for each test unit. diff --git a/browser/components/extensions/test/xpcshell/head.js b/browser/components/extensions/test/xpcshell/head.js index 6c4ce36871a6..9d9a087b3e6c 100644 --- a/browser/components/extensions/test/xpcshell/head.js +++ b/browser/components/extensions/test/xpcshell/head.js @@ -4,6 +4,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; /* exported createHttpServer, promiseConsoleOutput */ +Components.utils.import("resource://gre/modules/Services.jsm"); Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "AppConstants", @@ -24,11 +25,11 @@ XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Schemas", "resource://gre/modules/Schemas.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Services", - "resource://gre/modules/Services.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "TestUtils", "resource://testing-common/TestUtils.jsm"); +Services.prefs.setBoolPref("extensions.webextensions.remote", false); + ExtensionTestUtils.init(this); diff --git a/browser/components/extensions/test/xpcshell/head_remote.js b/browser/components/extensions/test/xpcshell/head_remote.js new file mode 100644 index 000000000000..35eb498f5083 --- /dev/null +++ b/browser/components/extensions/test/xpcshell/head_remote.js @@ -0,0 +1,4 @@ +"use strict"; + +Services.prefs.setBoolPref("extensions.webextensions.remote", true); +Services.prefs.setIntPref("dom.ipc.keepProcessesAlive.extension", 1); diff --git a/browser/components/extensions/test/xpcshell/xpcshell-common.ini b/browser/components/extensions/test/xpcshell/xpcshell-common.ini new file mode 100644 index 000000000000..0919d94fdf4c --- /dev/null +++ b/browser/components/extensions/test/xpcshell/xpcshell-common.ini @@ -0,0 +1,8 @@ +[test_ext_bookmarks.js] +[test_ext_browsingData.js] +[test_ext_browsingData_cookies_cache.js] +[test_ext_browsingData_downloads.js] +[test_ext_browsingData_passwords.js] +[test_ext_browsingData_settings.js] +[test_ext_history.js] +[test_ext_geckoProfiler_control.js] diff --git a/browser/components/extensions/test/xpcshell/xpcshell-remote.ini b/browser/components/extensions/test/xpcshell/xpcshell-remote.ini new file mode 100644 index 000000000000..3d6343394174 --- /dev/null +++ b/browser/components/extensions/test/xpcshell/xpcshell-remote.ini @@ -0,0 +1,7 @@ +[DEFAULT] +head = head.js head_remote.js +firefox-appdir = browser +tags = webextensions remote-webextensions +dupe-manifest = + +[include:xpcshell-common.ini] diff --git a/browser/components/extensions/test/xpcshell/xpcshell.ini b/browser/components/extensions/test/xpcshell/xpcshell.ini index 770f7c2f323e..107e45d92a75 100644 --- a/browser/components/extensions/test/xpcshell/xpcshell.ini +++ b/browser/components/extensions/test/xpcshell/xpcshell.ini @@ -1,16 +1,23 @@ [DEFAULT] head = head.js firefox-appdir = browser -tags = webextensions +tags = webextensions in-process-webextensions +dupe-manifest = + +# This file contains tests which are not affected by multi-process +# configuration, or do not support out-of-process content or extensions +# for one reason or another. +# +# Tests which are affected by remote content or remote extensions should +# go in one of: +# +# - xpcshell-common.ini +# For tests which should run in all configurations. +# - xpcshell-remote.ini +# For tests which should only run with both remote extensions and remote content. -[test_ext_bookmarks.js] -[test_ext_browsingData.js] -[test_ext_browsingData_cookies_cache.js] -[test_ext_browsingData_downloads.js] -[test_ext_browsingData_passwords.js] -[test_ext_browsingData_settings.js] -[test_ext_history.js] [test_ext_manifest_commands.js] [test_ext_manifest_omnibox.js] [test_ext_manifest_permissions.js] -[test_ext_geckoProfiler_control.js] + +[include:xpcshell-common.ini] diff --git a/devtools/client/inspector/boxmodel/box-model.js b/devtools/client/inspector/boxmodel/box-model.js index 706d53bdfaa7..cc3f1e435838 100644 --- a/devtools/client/inspector/boxmodel/box-model.js +++ b/devtools/client/inspector/boxmodel/box-model.js @@ -127,16 +127,19 @@ BoxModel.prototype = { } let lastRequest = Task.spawn((function* () { - if (!(this.isPanelVisible() && - this.inspector.selection.isConnected() && - this.inspector.selection.isElementNode())) { + if (!this.inspector || + !this.isPanelVisible() || + !this.inspector.selection.isConnected() || + !this.inspector.selection.isElementNode()) { return null; } let node = this.inspector.selection.nodeFront; + let layout = yield this.inspector.pageStyle.getLayout(node, { autoMargins: true, }); + let styleEntries = yield this.inspector.pageStyle.getApplied(node, { // We don't need styles applied to pseudo elements of the current node. skipPseudo: true @@ -146,12 +149,13 @@ BoxModel.prototype = { // Update the layout properties with whether or not the element's position is // editable with the geometry editor. let isPositionEditable = yield this.inspector.pageStyle.isPositionEditable(node); + layout = Object.assign({}, layout, { isPositionEditable, }); - const actorCanGetOffSetParent - = yield this.inspector.target.actorHasMethod("domwalker", "getOffsetParent"); + const actorCanGetOffSetParent = + yield this.inspector.target.actorHasMethod("domwalker", "getOffsetParent"); if (actorCanGetOffSetParent) { // Update the redux store with the latest offset parent DOM node diff --git a/devtools/client/inspector/grids/components/GridDisplaySettings.js b/devtools/client/inspector/grids/components/GridDisplaySettings.js index 29b3bf6b15d5..b565f02a964e 100644 --- a/devtools/client/inspector/grids/components/GridDisplaySettings.js +++ b/devtools/client/inspector/grids/components/GridDisplaySettings.js @@ -63,6 +63,7 @@ module.exports = createClass({ {}, dom.input( { + id: "grid-setting-extend-grid-lines", type: "checkbox", checked: highlighterSettings.showInfiniteLines, onChange: this.onShowInfiniteLinesCheckboxClick, @@ -79,6 +80,7 @@ module.exports = createClass({ {}, dom.input( { + id: "grid-setting-show-grid-line-numbers", type: "checkbox", checked: highlighterSettings.showGridLineNumbers, onChange: this.onShowGridLineNumbersCheckboxClick, diff --git a/devtools/client/inspector/grids/components/GridList.js b/devtools/client/inspector/grids/components/GridList.js index 43acaebdaaf3..f997408065ec 100644 --- a/devtools/client/inspector/grids/components/GridList.js +++ b/devtools/client/inspector/grids/components/GridList.js @@ -48,7 +48,9 @@ module.exports = createClass({ getStr("layout.overlayGrid") ), dom.ul( - {}, + { + id: "grid-list", + }, grids.map(grid => GridItem({ key: grid.id, getSwatchColorPickerTooltip, diff --git a/devtools/client/inspector/grids/grid-inspector.js b/devtools/client/inspector/grids/grid-inspector.js index 4b750016d792..5ef93b7cfd11 100644 --- a/devtools/client/inspector/grids/grid-inspector.js +++ b/devtools/client/inspector/grids/grid-inspector.js @@ -253,13 +253,27 @@ GridInspector.prototype = { // Get all the GridFront from the server if no gridFronts were provided. if (!gridFronts) { - gridFronts = yield this.layoutInspector.getAllGrids(this.walker.rootNode); + try { + gridFronts = yield this.layoutInspector.getAllGrids(this.walker.rootNode); + } catch (e) { + // This call might fail if called asynchrously after the toolbox is finished + // closing. + return; + } } let grids = []; for (let i = 0; i < gridFronts.length; i++) { let grid = gridFronts[i]; - let nodeFront = yield this.walker.getNodeFromActor(grid.actorID, ["containerEl"]); + + let nodeFront; + try { + nodeFront = yield this.walker.getNodeFromActor(grid.actorID, ["containerEl"]); + } catch (e) { + // This call might fail if called asynchrously after the toolbox is finished + // closing. + return; + } let fallbackColor = GRID_COLORS[i % GRID_COLORS.length]; let color = this.getInitialGridColor(nodeFront, fallbackColor); @@ -300,7 +314,7 @@ GridInspector.prototype = { * @param {Object} options * The highlighter options used for the highlighter being shown/hidden. */ - onHighlighterChange(event, nodeFront, options) { + onHighlighterChange(event, nodeFront, options = {}) { let highlighted = event === "grid-highlighter-shown"; let { color } = options; diff --git a/devtools/client/inspector/grids/moz.build b/devtools/client/inspector/grids/moz.build index 1b51bd039bfb..74ce577196a7 100644 --- a/devtools/client/inspector/grids/moz.build +++ b/devtools/client/inspector/grids/moz.build @@ -15,3 +15,5 @@ DevToolsModules( 'grid-inspector.js', 'types.js', ) + +BROWSER_CHROME_MANIFESTS += ['test/browser.ini'] diff --git a/devtools/client/inspector/grids/reducers/grids.js b/devtools/client/inspector/grids/reducers/grids.js index 63f88a45fede..6583c653fef7 100644 --- a/devtools/client/inspector/grids/reducers/grids.js +++ b/devtools/client/inspector/grids/reducers/grids.js @@ -17,7 +17,7 @@ let reducers = { [UPDATE_GRID_COLOR](grids, { nodeFront, color }) { let newGrids = grids.map(g => { if (g.nodeFront == nodeFront) { - g.color = color; + g = Object.assign({}, g, { color }); } return g; diff --git a/devtools/client/inspector/grids/test/.eslintrc.js b/devtools/client/inspector/grids/test/.eslintrc.js new file mode 100644 index 000000000000..b63e8411e3b7 --- /dev/null +++ b/devtools/client/inspector/grids/test/.eslintrc.js @@ -0,0 +1,9 @@ +"use strict"; + +module.exports = { + // Extend from the shared list of defined globals for mochitests. + "extends": "../../../../.eslintrc.mochitests.js", + "globals": { + "waitUntilState": true + } +}; diff --git a/devtools/client/inspector/grids/test/browser.ini b/devtools/client/inspector/grids/test/browser.ini new file mode 100644 index 000000000000..4fa238d2ddcc --- /dev/null +++ b/devtools/client/inspector/grids/test/browser.ini @@ -0,0 +1,23 @@ +[DEFAULT] +tags = devtools +subsuite = devtools +support-files = + head.js + !/devtools/client/commandline/test/helpers.js + !/devtools/client/framework/test/shared-head.js + !/devtools/client/inspector/test/head.js + !/devtools/client/inspector/test/shared-head.js + !/devtools/client/shared/test/test-actor.js + !/devtools/client/shared/test/test-actor-registry.js + !/devtools/client/framework/test/shared-redux-head.js + +[browser_grids_display-setting-extend-grid-lines.js] +[browser_grids_display-setting-show-grid-line-numbers.js] +[browser_grids_grid-list-color-picker-on-ESC.js] +[browser_grids_grid-list-color-picker-on-RETURN.js] +[browser_grids_grid-list-element-rep.js] +[browser_grids_grid-list-no-grids.js] +[browser_grids_grid-list-on-mutation-element-added.js] +[browser_grids_grid-list-on-mutation-element-removed.js] +[browser_grids_grid-list-toggle-multiple-grids.js] +[browser_grids_grid-list-toggle-single-grid.js] diff --git a/devtools/client/inspector/grids/test/browser_grids_display-setting-extend-grid-lines.js b/devtools/client/inspector/grids/test/browser_grids_display-setting-extend-grid-lines.js new file mode 100644 index 000000000000..e6b68647dcfb --- /dev/null +++ b/devtools/client/inspector/grids/test/browser_grids_display-setting-extend-grid-lines.js @@ -0,0 +1,51 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Tests that the 'Extend grid lines infinitely' grid highlighter setting will update +// the redux store and pref setting. + +const TEST_URI = ` + +
+
cell1
+
cell2
+
+`; + +const SHOW_INFINITE_LINES_PREF = "devtools.gridinspector.showInfiniteLines"; + +add_task(function* () { + yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); + let { inspector, gridInspector } = yield openLayoutView(); + let { document: doc } = gridInspector; + let { store } = inspector; + + yield selectNode("#grid", inspector); + let checkbox = doc.getElementById("grid-setting-extend-grid-lines"); + + ok(!Services.prefs.getBoolPref(SHOW_INFINITE_LINES_PREF), + "'Extend grid lines infinitely' is pref off by default."); + + info("Toggling ON the 'Extend grid lines infinitely' setting."); + let onCheckboxChange = waitUntilState(store, state => + state.highlighterSettings.showInfiniteLines); + checkbox.click(); + yield onCheckboxChange; + + info("Toggling OFF the 'Extend grid lines infinitely' setting."); + onCheckboxChange = waitUntilState(store, state => + !state.highlighterSettings.showInfiniteLines); + checkbox.click(); + yield onCheckboxChange; + + ok(!Services.prefs.getBoolPref(SHOW_INFINITE_LINES_PREF), + "'Extend grid lines infinitely' is pref off."); + + Services.prefs.clearUserPref(SHOW_INFINITE_LINES_PREF); +}); diff --git a/devtools/client/inspector/grids/test/browser_grids_display-setting-show-grid-line-numbers.js b/devtools/client/inspector/grids/test/browser_grids_display-setting-show-grid-line-numbers.js new file mode 100644 index 000000000000..b5c9a5a50af7 --- /dev/null +++ b/devtools/client/inspector/grids/test/browser_grids_display-setting-show-grid-line-numbers.js @@ -0,0 +1,55 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Tests that the 'Display numbers on lines' grid highlighter setting will update +// the redux store and pref setting. + +const TEST_URI = ` + +
+
cell1
+
cell2
+
+`; + +const SHOW_GRID_LINE_NUMBERS = "devtools.gridinspector.showGridLineNumbers"; + +add_task(function* () { + yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); + let { inspector, gridInspector } = yield openLayoutView(); + let { document: doc } = gridInspector; + let { store } = inspector; + + yield selectNode("#grid", inspector); + let checkbox = doc.getElementById("grid-setting-show-grid-line-numbers"); + + info("Checking the initial state of the CSS grid highlighter setting."); + ok(!Services.prefs.getBoolPref(SHOW_GRID_LINE_NUMBERS), + "'Display numbers on lines' is pref off by default."); + + info("Toggling ON the 'Display numbers on lines' setting."); + let onCheckboxChange = waitUntilState(store, state => + state.highlighterSettings.showGridLineNumbers); + checkbox.click(); + yield onCheckboxChange; + + ok(Services.prefs.getBoolPref(SHOW_GRID_LINE_NUMBERS), + "'Display numbers on lines' is pref on."); + + info("Toggling OFF the 'Display numbers on lines' setting."); + onCheckboxChange = waitUntilState(store, state => + !state.highlighterSettings.showGridLineNumbers); + checkbox.click(); + yield onCheckboxChange; + + ok(!Services.prefs.getBoolPref(SHOW_GRID_LINE_NUMBERS), + "'Display numbers on lines' is pref off."); + + Services.prefs.clearUserPref(SHOW_GRID_LINE_NUMBERS); +}); diff --git a/devtools/client/inspector/grids/test/browser_grids_grid-list-color-picker-on-ESC.js b/devtools/client/inspector/grids/test/browser_grids_grid-list-color-picker-on-ESC.js new file mode 100644 index 000000000000..c182c6d1cd8e --- /dev/null +++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-color-picker-on-ESC.js @@ -0,0 +1,58 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Tests that the grid item's color change in the colorpicker is reverted when ESCAPE is +// pressed. + +const TEST_URI = ` + +
+
cell1
+
cell2
+
+`; + +add_task(function* () { + yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); + let { inspector, gridInspector } = yield openLayoutView(); + let { document: doc } = gridInspector; + let { store } = inspector; + let cPicker = gridInspector.getSwatchColorPickerTooltip(); + let spectrum = cPicker.spectrum; + let swatch = doc.querySelector(".grid-color-swatch"); + + info("Checking the initial state of the Grid Inspector."); + is(swatch.style.backgroundColor, "rgb(75, 0, 130)", + "The color swatch's background is correct."); + is(store.getState().grids[0].color, "#4B0082", "The grid color state is correct."); + + info("Scrolling into view of the #grid color swatch."); + swatch.scrollIntoView(); + + info("Opening the color picker by clicking on the #grid color swatch."); + let onColorPickerReady = cPicker.once("ready"); + swatch.click(); + yield onColorPickerReady; + + yield simulateColorPickerChange(cPicker, [0, 255, 0, .5]); + + is(swatch.style.backgroundColor, "rgba(0, 255, 0, 0.5)", + "The color swatch's background was updated."); + + info("Pressing ESCAPE to close the tooltip."); + let onGridColorUpdate = waitUntilState(store, state => + state.grids[0].color === "#4B0082"); + let onColorPickerHidden = cPicker.tooltip.once("hidden"); + focusAndSendKey(spectrum.element.ownerDocument.defaultView, "ESCAPE"); + yield onColorPickerHidden; + yield onGridColorUpdate; + + is(swatch.style.backgroundColor, "rgb(75, 0, 130)", + "The color swatch's background was reverted after ESCAPE."); +}); diff --git a/devtools/client/inspector/grids/test/browser_grids_grid-list-color-picker-on-RETURN.js b/devtools/client/inspector/grids/test/browser_grids_grid-list-color-picker-on-RETURN.js new file mode 100644 index 000000000000..e89976617e8e --- /dev/null +++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-color-picker-on-RETURN.js @@ -0,0 +1,58 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Tests that the grid item's color change in the colorpicker is committed when RETURN is +// pressed. + +const TEST_URI = ` + +
+
cell1
+
cell2
+
+`; + +add_task(function* () { + yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); + let { inspector, gridInspector } = yield openLayoutView(); + let { document: doc } = gridInspector; + let { store } = inspector; + let cPicker = gridInspector.getSwatchColorPickerTooltip(); + let spectrum = cPicker.spectrum; + let swatch = doc.querySelector(".grid-color-swatch"); + + info("Checking the initial state of the Grid Inspector."); + is(swatch.style.backgroundColor, "rgb(75, 0, 130)", + "The color swatch's background is correct."); + is(store.getState().grids[0].color, "#4B0082", "The grid color state is correct."); + + info("Scrolling into view of the #grid color swatch."); + swatch.scrollIntoView(); + + info("Opening the color picker by clicking on the #grid color swatch."); + let onColorPickerReady = cPicker.once("ready"); + swatch.click(); + yield onColorPickerReady; + + yield simulateColorPickerChange(cPicker, [0, 255, 0, .5]); + + is(swatch.style.backgroundColor, "rgba(0, 255, 0, 0.5)", + "The color swatch's background was updated."); + + info("Pressing RETURN to commit the color change."); + let onGridColorUpdate = waitUntilState(store, state => + state.grids[0].color === "#00FF0080"); + let onColorPickerHidden = cPicker.tooltip.once("hidden"); + focusAndSendKey(spectrum.element.ownerDocument.defaultView, "RETURN"); + yield onColorPickerHidden; + yield onGridColorUpdate; + + is(swatch.style.backgroundColor, "rgba(0, 255, 0, 0.5)", + "The color swatch's background was kept after RETURN."); +}); diff --git a/devtools/client/inspector/grids/test/browser_grids_grid-list-element-rep.js b/devtools/client/inspector/grids/test/browser_grids_grid-list-element-rep.js new file mode 100644 index 000000000000..f6b3a8d3b161 --- /dev/null +++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-element-rep.js @@ -0,0 +1,49 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Tests that the grid item's element rep will display the box model higlighter on hover +// and select the node on click. + +const TEST_URI = ` + +
+
cell1
+
cell2
+
+`; + +add_task(function* () { + yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); + let { inspector, gridInspector, toolbox } = yield openLayoutView(); + let { document: doc } = gridInspector; + let { store } = inspector; + + let gridList = doc.querySelector("#grid-list"); + let elementRep = gridList.children[0].querySelector(".open-inspector"); + info("Scrolling into the view the #grid element node rep."); + elementRep.scrollIntoView(); + + info("Listen to node-highlight event and mouse over the widget"); + let onHighlight = toolbox.once("node-highlight"); + EventUtils.synthesizeMouse(elementRep, 10, 5, {type: "mouseover"}, doc.defaultView); + let nodeFront = yield onHighlight; + + ok(nodeFront, "nodeFront was returned from highlighting the node."); + is(nodeFront.tagName, "DIV", "The highlighted node has the correct tagName."); + is(nodeFront.attributes[0].name, "id", + "The highlighted node has the correct attributes."); + is(nodeFront.attributes[0].value, "grid", "The highlighted node has the correct id."); + + let onSelection = inspector.selection.once("new-node-front"); + EventUtils.sendMouseEvent({type: "click"}, elementRep, doc.defaultView); + yield onSelection; + + is(inspector.selection.nodeFront, store.getState().grids[0].nodeFront, + "The selected node is the one stored on the grid item's state."); +}); diff --git a/devtools/client/inspector/grids/test/browser_grids_grid-list-no-grids.js b/devtools/client/inspector/grids/test/browser_grids_grid-list-no-grids.js new file mode 100644 index 000000000000..73ec6ddac545 --- /dev/null +++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-no-grids.js @@ -0,0 +1,34 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Tests that no grid list items and a "no grids available" message is displayed when +// there are no grid containers on the page. + +const TEST_URI = ` + +
+
cell1
+
cell2
+
+`; + +add_task(function* () { + yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); + let { inspector, gridInspector } = yield openLayoutView(); + let { document: doc } = gridInspector; + let { highlighters } = inspector; + + yield selectNode("#grid", inspector); + let noGridList = doc.querySelector(".layout-no-grids"); + let gridList = doc.querySelector("#grid-list"); + + info("Checking the initial state of the Grid Inspector."); + ok(noGridList, "The message no grid containers is displayed."); + ok(!gridList, "No grid containers are listed."); + ok(!highlighters.highlighters[HIGHLIGHTER_TYPE], + "No CSS grid highlighter exists in the highlighters overlay."); + ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); +}); diff --git a/devtools/client/inspector/grids/test/browser_grids_grid-list-on-mutation-element-added.js b/devtools/client/inspector/grids/test/browser_grids_grid-list-on-mutation-element-added.js new file mode 100644 index 000000000000..b1429398939a --- /dev/null +++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-on-mutation-element-added.js @@ -0,0 +1,93 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Tests that the grid list updates when a new grid container is added to the page. + +const TEST_URI = ` + +
+
cell1
+
cell2
+
+
+
cell1
+
cell2
+
+`; + +add_task(function* () { + yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); + let { inspector, gridInspector, testActor } = yield openLayoutView(); + let { document: doc } = gridInspector; + let { highlighters, store } = inspector; + + yield selectNode("#grid", inspector); + let gridList = doc.querySelector("#grid-list"); + let checkbox1 = gridList.children[0].querySelector("input"); + + info("Checking the initial state of the Grid Inspector."); + is(gridList.childNodes.length, 1, "One grid container is listed."); + ok(!highlighters.highlighters[HIGHLIGHTER_TYPE], + "No CSS grid highlighter exists in the highlighters overlay."); + ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); + + info("Toggling ON the CSS grid highlighter from the layout panel."); + let onHighlighterShown = highlighters.once("grid-highlighter-shown"); + checkbox1.click(); + yield onHighlighterShown; + + info("Checking the CSS grid highlighter is created."); + ok(highlighters.highlighters[HIGHLIGHTER_TYPE], + "CSS grid highlighter is created in the highlighters overlay."); + ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); + + info("Adding the #grid2 container in the content page."); + let onGridListUpdate = waitUntilState(store, state => + state.grids.length == 2 && + state.grids[0].highlighted && + !state.grids[1].highlighted); + testActor.eval(` + content.document.getElementById("grid2").classList.add("grid"); + `); + yield onGridListUpdate; + + info("Checking the new Grid Inspector state."); + is(gridList.childNodes.length, 2, "Two grid containers are listed."); + ok(highlighters.highlighters[HIGHLIGHTER_TYPE], + "CSS grid highlighter is created in the highlighters overlay."); + ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); + + let checkbox2 = gridList.children[1].querySelector("input"); + + info("Toggling ON the CSS grid highlighter for #grid2."); + onHighlighterShown = highlighters.once("grid-highlighter-shown"); + let onCheckboxChange = waitUntilState(store, state => + state.grids.length == 2 && + !state.grids[0].highlighted && + state.grids[1].highlighted); + checkbox2.click(); + yield onHighlighterShown; + yield onCheckboxChange; + + info("Checking the CSS grid highlighter is still shown."); + ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); + + info("Toggling OFF the CSS grid highlighter from the layout panel."); + let onHighlighterHidden = highlighters.once("grid-highlighter-hidden"); + onCheckboxChange = waitUntilState(store, state => + state.grids.length == 2 && + !state.grids[0].highlighted && + !state.grids[1].highlighted); + checkbox2.click(); + yield onHighlighterHidden; + yield onCheckboxChange; + + info("Checking the CSS grid highlighter is not shown."); + ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); +}); diff --git a/devtools/client/inspector/grids/test/browser_grids_grid-list-on-mutation-element-removed.js b/devtools/client/inspector/grids/test/browser_grids_grid-list-on-mutation-element-removed.js new file mode 100644 index 000000000000..99cfaaf9ecaa --- /dev/null +++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-on-mutation-element-removed.js @@ -0,0 +1,63 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Tests that the grid item is removed from the grid list when the grid container is +// removed from the page. + +const TEST_URI = ` + +
+
cell1
+
cell2
+
+`; + +add_task(function* () { + yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); + let { inspector, gridInspector, testActor } = yield openLayoutView(); + let { document: doc } = gridInspector; + let { highlighters, store } = inspector; + + yield selectNode("#grid", inspector); + let gridList = doc.querySelector("#grid-list"); + let checkbox = gridList.children[0].querySelector("input"); + + info("Checking the initial state of the Grid Inspector."); + is(gridList.childNodes.length, 1, "One grid container is listed."); + ok(!highlighters.highlighters[HIGHLIGHTER_TYPE], + "No CSS grid highlighter exists in the highlighters overlay."); + ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); + + info("Toggling ON the CSS grid highlighter from the layout panel."); + let onHighlighterShown = highlighters.once("grid-highlighter-shown"); + let onCheckboxChange = waitUntilState(store, state => + state.grids.length == 1 && state.grids[0].highlighted); + checkbox.click(); + yield onHighlighterShown; + yield onCheckboxChange; + + info("Checking the CSS grid highlighter is created."); + ok(highlighters.highlighters[HIGHLIGHTER_TYPE], + "CSS grid highlighter is created in the highlighters overlay."); + ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); + + info("Removing the #grid container in the content page."); + let onHighlighterHidden = highlighters.once("grid-highlighter-hidden"); + onCheckboxChange = waitUntilState(store, state => state.grids.length == 0); + testActor.eval(` + content.document.getElementById("grid").remove(); + `); + yield onHighlighterHidden; + yield onCheckboxChange; + + info("Checking the CSS grid highlighter is not shown."); + ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); + let noGridList = doc.querySelector(".layout-no-grids"); + ok(noGridList, "The message no grid containers is displayed."); +}); diff --git a/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-multiple-grids.js b/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-multiple-grids.js new file mode 100644 index 000000000000..b2e21d010e6b --- /dev/null +++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-multiple-grids.js @@ -0,0 +1,84 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test toggling the grid highlighter in the grid inspector panel with multiple grids in +// the page. + +const TEST_URI = ` + +
+
cell1
+
cell2
+
+
+
cell1
+
cell2
+
+`; + +add_task(function* () { + yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); + let { inspector, gridInspector } = yield openLayoutView(); + let { document: doc } = gridInspector; + let { highlighters, store } = inspector; + + yield selectNode("#grid1", inspector); + let gridList = doc.querySelector("#grid-list"); + let checkbox1 = gridList.children[0].querySelector("input"); + let checkbox2 = gridList.children[1].querySelector("input"); + + info("Checking the initial state of the Grid Inspector."); + is(gridList.childNodes.length, 2, "2 grid containers are listed."); + ok(!checkbox1.checked, `Grid item ${checkbox1.value} is unchecked in the grid list.`); + ok(!checkbox2.checked, `Grid item ${checkbox2.value} is unchecked in the grid list.`); + ok(!highlighters.highlighters[HIGHLIGHTER_TYPE], + "No CSS grid highlighter exists in the highlighters overlay."); + ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); + + info("Toggling ON the CSS grid highlighter for #grid1."); + let onHighlighterShown = highlighters.once("grid-highlighter-shown"); + let onCheckboxChange = waitUntilState(store, state => + state.grids.length == 2 && + state.grids[0].highlighted && + !state.grids[1].highlighted); + checkbox1.click(); + yield onHighlighterShown; + yield onCheckboxChange; + + info("Checking the CSS grid highlighter is created."); + ok(highlighters.highlighters[HIGHLIGHTER_TYPE], + "CSS grid highlighter is created in the highlighters overlay."); + ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); + + info("Toggling ON the CSS grid highlighter for #grid2."); + onHighlighterShown = highlighters.once("grid-highlighter-shown"); + onCheckboxChange = waitUntilState(store, state => + state.grids.length == 2 && + !state.grids[0].highlighted && + state.grids[1].highlighted); + checkbox2.click(); + yield onHighlighterShown; + yield onCheckboxChange; + + info("Checking the CSS grid highlighter is still shown."); + ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); + + info("Toggling OFF the CSS grid highlighter from the layout panel."); + let onHighlighterHidden = highlighters.once("grid-highlighter-hidden"); + onCheckboxChange = waitUntilState(store, state => + state.grids.length == 2 && + !state.grids[0].highlighted && + !state.grids[1].highlighted); + checkbox2.click(); + yield onHighlighterHidden; + yield onCheckboxChange; + + info("Checking the CSS grid highlighter is not shown."); + ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); +}); diff --git a/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-single-grid.js b/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-single-grid.js new file mode 100644 index 000000000000..f7653a34bffe --- /dev/null +++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-single-grid.js @@ -0,0 +1,62 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Tests toggling ON/OFF the grid highlighter from the grid ispector panel. + +const TEST_URI = ` + +
+
cell1
+
cell2
+
+`; + +add_task(function* () { + yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); + let { gridInspector, inspector } = yield openLayoutView(); + let { document: doc } = gridInspector; + let { highlighters, store } = inspector; + + yield selectNode("#grid", inspector); + let gridList = doc.querySelector("#grid-list"); + let checkbox = gridList.children[0].querySelector("input"); + + info("Checking the initial state of the Grid Inspector."); + is(gridList.childNodes.length, 1, "One grid container is listed."); + ok(!checkbox.checked, `Grid item ${checkbox.value} is unchecked in the grid list.`); + ok(!highlighters.highlighters[HIGHLIGHTER_TYPE], + "No CSS grid highlighter exists in the highlighters overlay."); + ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); + + info("Toggling ON the CSS grid highlighter from the layout panel."); + let onHighlighterShown = highlighters.once("grid-highlighter-shown"); + let onCheckboxChange = waitUntilState(store, state => + state.grids.length == 1 && + state.grids[0].highlighted); + checkbox.click(); + yield onHighlighterShown; + yield onCheckboxChange; + + info("Checking the CSS grid highlighter is created."); + ok(highlighters.highlighters[HIGHLIGHTER_TYPE], + "CSS grid highlighter is created in the highlighters overlay."); + ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown."); + + info("Toggling OFF the CSS grid highlighter from the layout panel."); + let onHighlighterHidden = highlighters.once("grid-highlighter-hidden"); + onCheckboxChange = waitUntilState(store, state => + state.grids.length == 1 && + !state.grids[0].highlighted); + checkbox.click(); + yield onHighlighterHidden; + yield onCheckboxChange; + + info("Checking the CSS grid highlighter is not shown."); + ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown."); +}); diff --git a/devtools/client/inspector/grids/test/head.js b/devtools/client/inspector/grids/test/head.js new file mode 100644 index 000000000000..a00ae91fb2dd --- /dev/null +++ b/devtools/client/inspector/grids/test/head.js @@ -0,0 +1,74 @@ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint no-unused-vars: [2, {"vars": "local"}] */ +/* import-globals-from ../../../framework/test/shared-head.js */ +/* import-globals-from ../../test/head.js */ +"use strict"; + +// Import the inspector's head.js first (which itself imports shared-head.js). +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/devtools/client/inspector/test/head.js", + this); + +// Load the shared Redux helpers into this compartment. +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/devtools/client/framework/test/shared-redux-head.js", + this); + +Services.prefs.setBoolPref("devtools.layoutview.enabled", true); +Services.prefs.setIntPref("devtools.toolbox.footer.height", 350); +registerCleanupFunction(() => { + Services.prefs.clearUserPref("devtools.layoutview.enabled"); + Services.prefs.clearUserPref("devtools.toolbox.footer.height"); +}); + +const HIGHLIGHTER_TYPE = "CssGridHighlighter"; + +/** + * Open the toolbox, with the inspector tool visible, and the layout view + * sidebar tab selected to display the box model view with properties. + * + * @return {Promise} a promise that resolves when the inspector is ready and the box model + * view is visible and ready. + */ +function openLayoutView() { + return openInspectorSidebarTab("layoutview").then(data => { + // The actual highligher show/hide methods are mocked in box model tests. + // The highlighter is tested in devtools/inspector/test. + function mockHighlighter({highlighter}) { + highlighter.showBoxModel = function () { + return promise.resolve(); + }; + highlighter.hideBoxModel = function () { + return promise.resolve(); + }; + } + mockHighlighter(data.toolbox); + + return { + toolbox: data.toolbox, + inspector: data.inspector, + gridInspector: data.inspector.gridInspector, + testActor: data.testActor + }; + }); +} + +/** + * Simulate a color change in a given color picker tooltip. + * + * @param {Spectrum|ColorWidget} colorPicker + * The color picker widget. + * @param {Array} newRgba + * Array of the new rgba values to be set in the color widget. + */ +var simulateColorPickerChange = Task.async(function* (colorPicker, newRgba) { + info("Getting the spectrum colorpicker object"); + let spectrum = yield colorPicker.spectrum; + info("Setting the new color"); + spectrum.rgb = newRgba; + info("Applying the change"); + spectrum.updateUI(); + spectrum.onChange(); +}); diff --git a/devtools/client/inspector/rules/test/head.js b/devtools/client/inspector/rules/test/head.js index 55f7b71e88a2..0d8f9f88a937 100644 --- a/devtools/client/inspector/rules/test/head.js +++ b/devtools/client/inspector/rules/test/head.js @@ -497,16 +497,6 @@ function* clickSelectorIcon(icon, view) { yield onToggled; } -/** - * Make sure window is properly focused before sending a key event. - * @param {Window} win - * @param {Event} key - */ -function focusAndSendKey(win, key) { - win.document.documentElement.focus(); - EventUtils.sendKey(key, win); -} - /** * Toggle one of the checkboxes inside the class-panel. Resolved after the DOM mutation * has been recorded. diff --git a/devtools/client/inspector/shared/highlighters-overlay.js b/devtools/client/inspector/shared/highlighters-overlay.js index 39add94725eb..d975d23f7ba1 100644 --- a/devtools/client/inspector/shared/highlighters-overlay.js +++ b/devtools/client/inspector/shared/highlighters-overlay.js @@ -6,7 +6,6 @@ "use strict"; -const promise = require("promise"); const {Task} = require("devtools/shared/task"); const EventEmitter = require("devtools/shared/event-emitter"); const { VIEW_NODE_VALUE_TYPE } = require("devtools/client/inspector/shared/node-types"); @@ -267,18 +266,26 @@ HighlightersOverlay.prototype = { * The highlighter type. One of this.highlighters. * @return {Promise} that resolves to the highlighter */ - _getHighlighter: function (type) { + _getHighlighter: Task.async(function* (type) { let utils = this.highlighterUtils; if (this.highlighters[type]) { - return promise.resolve(this.highlighters[type]); + return this.highlighters[type]; } - return utils.getHighlighterByType(type).then(highlighter => { - this.highlighters[type] = highlighter; - return highlighter; - }); - }, + let highlighter; + + try { + highlighter = yield utils.getHighlighterByType(type); + } catch (e) {} + + if (!highlighter) { + return null; + } + + this.highlighters[type] = highlighter; + return highlighter; + }), _handleRejection: function (error) { if (!this.destroyed) { diff --git a/devtools/client/inspector/test/head.js b/devtools/client/inspector/test/head.js index 2eb6f44ababc..c5cd9a8fd046 100644 --- a/devtools/client/inspector/test/head.js +++ b/devtools/client/inspector/test/head.js @@ -643,6 +643,19 @@ function synthesizeKeys(input, win) { } } +/** + * Make sure window is properly focused before sending a key event. + * + * @param {Window} win + * The window containing the panel + * @param {String} key + * The string value to input + */ +function focusAndSendKey(win, key) { + win.document.documentElement.focus(); + EventUtils.sendKey(key, win); +} + /** * Given a Tooltip instance, fake a mouse event on the `target` DOM Element * and assert that the `tooltip` is correctly displayed. diff --git a/dom/animation/test/chrome/test_restyles.html b/dom/animation/test/chrome/test_restyles.html index de94f3bb6cb6..370e8db04dd2 100644 --- a/dom/animation/test/chrome/test_restyles.html +++ b/dom/animation/test/chrome/test_restyles.html @@ -62,6 +62,7 @@ function ensureElementRemoval(aElement) { }); } +SimpleTest.expectAssertions(0, 1); // bug 1332970 SimpleTest.waitForExplicitFinish(); var omtaEnabled = isOMTAEnabled(); diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 86b6ed96ec79..2b373a075dea 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -10010,9 +10010,9 @@ nsIDocument::RegisterPendingLinkUpdate(Link* aLink) if (!mHasLinksToUpdateRunnable) { nsCOMPtr event = NewRunnableMethod(this, &nsIDocument::FlushPendingLinkUpdatesFromRunnable); + // Do this work in a second in the worst case. nsresult rv = - Dispatch("nsIDocument::FlushPendingLinkUpdatesFromRunnable", - TaskCategory::Other, event.forget()); + NS_IdleDispatchToCurrentThread(event.forget(), 1000); if (NS_FAILED(rv)) { // If during shutdown posting a runnable doesn't succeed, we probably // don't need to update link states. diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 1f3249b8f0c2..1c5839f92d17 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -867,7 +867,11 @@ nsGlobalWindow::ExecuteIdleRequest(TimeStamp aDeadline) mIdleRequestExecutor->MaybeUpdateIdlePeriodLimit(); nsresult result = RunIdleRequest(request, deadline, false); - mIdleRequestExecutor->MaybeDispatch(); + // Running the idle callback could've suspended the window, in which + // case mIdleRequestExecutor will be null. + if (mIdleRequestExecutor) { + mIdleRequestExecutor->MaybeDispatch(); + } return result; } diff --git a/dom/base/nsJSUtils.cpp b/dom/base/nsJSUtils.cpp index 424d97340f5f..d11bbf6d2e4b 100644 --- a/dom/base/nsJSUtils.cpp +++ b/dom/base/nsJSUtils.cpp @@ -146,6 +146,7 @@ nsJSUtils::ExecutionContext::ExecutionContext(JSContext* aCx, , mRv(NS_OK) , mSkip(false) , mCoerceToString(false) + , mEncodeBytecode(false) #ifdef DEBUG , mWantsReturnValue(false) , mExpectScopeChain(false) @@ -204,7 +205,19 @@ nsJSUtils::ExecutionContext::JoinAndExec(void **aOffThreadToken, MOZ_ASSERT(!mExpectScopeChain); aScript.set(JS::FinishOffThreadScript(mCx, *aOffThreadToken)); *aOffThreadToken = nullptr; // Mark the token as having been finished. - if (!aScript || !JS_ExecuteScript(mCx, mScopeChain, aScript)) { + if (!aScript) { + mSkip = true; + mRv = EvaluationExceptionToNSResult(mCx); + return mRv; + } + + if (mEncodeBytecode && !StartIncrementalEncoding(mCx, aScript)) { + mSkip = true; + mRv = EvaluationExceptionToNSResult(mCx); + return mRv; + } + + if (!JS_ExecuteScript(mCx, mScopeChain, aScript)) { mSkip = true; mRv = EvaluationExceptionToNSResult(mCx); return mRv; @@ -215,7 +228,8 @@ nsJSUtils::ExecutionContext::JoinAndExec(void **aOffThreadToken, nsresult nsJSUtils::ExecutionContext::CompileAndExec(JS::CompileOptions& aCompileOptions, - JS::SourceBufferHolder& aSrcBuf) + JS::SourceBufferHolder& aSrcBuf, + JS::MutableHandle aScript) { if (mSkip) { return mRv; @@ -228,8 +242,29 @@ nsJSUtils::ExecutionContext::CompileAndExec(JS::CompileOptions& aCompileOptions, #ifdef DEBUG mWantsReturnValue = !aCompileOptions.noScriptRval; #endif + + bool compiled = true; + if (mScopeChain.length() == 0) { + compiled = JS::Compile(mCx, aCompileOptions, aSrcBuf, aScript); + } else { + compiled = JS::CompileForNonSyntacticScope(mCx, aCompileOptions, aSrcBuf, aScript); + } + + MOZ_ASSERT_IF(compiled, aScript); + if (!compiled) { + mSkip = true; + mRv = EvaluationExceptionToNSResult(mCx); + return mRv; + } + + if (mEncodeBytecode && !StartIncrementalEncoding(mCx, aScript)) { + mSkip = true; + mRv = EvaluationExceptionToNSResult(mCx); + return mRv; + } + MOZ_ASSERT(!mCoerceToString || mWantsReturnValue); - if (!JS::Evaluate(mCx, mScopeChain, aCompileOptions, aSrcBuf, &mRetValue)) { + if (!JS_ExecuteScript(mCx, mScopeChain, aScript, &mRetValue)) { mSkip = true; mRv = EvaluationExceptionToNSResult(mCx); return mRv; @@ -242,6 +277,7 @@ nsresult nsJSUtils::ExecutionContext::CompileAndExec(JS::CompileOptions& aCompileOptions, const nsAString& aScript) { + MOZ_ASSERT(!mEncodeBytecode, "A JSScript is needed for calling FinishIncrementalEncoding"); if (mSkip) { return mRv; } @@ -249,7 +285,8 @@ nsJSUtils::ExecutionContext::CompileAndExec(JS::CompileOptions& aCompileOptions, const nsPromiseFlatString& flatScript = PromiseFlatString(aScript); JS::SourceBufferHolder srcBuf(flatScript.get(), aScript.Length(), JS::SourceBufferHolder::NoOwnership); - return CompileAndExec(aCompileOptions, srcBuf); + JS::Rooted script(mCx); + return CompileAndExec(aCompileOptions, srcBuf, &script); } nsresult @@ -257,6 +294,7 @@ nsJSUtils::ExecutionContext::DecodeAndExec(JS::CompileOptions& aCompileOptions, mozilla::Vector& aBytecodeBuf, size_t aBytecodeIndex) { + MOZ_ASSERT(!mEncodeBytecode, "A JSScript is needed for calling FinishIncrementalEncoding"); if (mSkip) { return mRv; } @@ -305,34 +343,6 @@ nsJSUtils::ExecutionContext::DecodeJoinAndExec(void **aOffThreadToken) return NS_OK; } -nsresult -nsJSUtils::ExecutionContext::JoinEncodeAndExec(void **aOffThreadToken, - JS::MutableHandle aScript) -{ - MOZ_ASSERT_IF(aOffThreadToken, !mWantsReturnValue); - aScript.set(JS::FinishOffThreadScript(mCx, *aOffThreadToken)); - *aOffThreadToken = nullptr; // Mark the token as having been finished. - if (!aScript) { - mSkip = true; - mRv = EvaluationExceptionToNSResult(mCx); - return mRv; - } - - if (!StartIncrementalEncoding(mCx, aScript)) { - mSkip = true; - mRv = EvaluationExceptionToNSResult(mCx); - return mRv; - } - - if (!JS_ExecuteScript(mCx, mScopeChain, aScript)) { - mSkip = true; - mRv = EvaluationExceptionToNSResult(mCx); - return mRv; - } - - return mRv; -} - nsresult nsJSUtils::ExecutionContext::ExtractReturnValue(JS::MutableHandle aRetValue) { diff --git a/dom/base/nsJSUtils.h b/dom/base/nsJSUtils.h index 834353a51016..027cd3cfa6f0 100644 --- a/dom/base/nsJSUtils.h +++ b/dom/base/nsJSUtils.h @@ -95,6 +95,9 @@ public: // Should the result be serialized before being returned. bool mCoerceToString; + // Encode the bytecode before it is being executed. + bool mEncodeBytecode; + #ifdef DEBUG // Should we set the return value. bool mWantsReturnValue; @@ -124,6 +127,15 @@ public: return *this; } + // When set, this flag records and encodes the bytecode as soon as it is + // being compiled, and before it is being executed. The bytecode can then be + // requested by using |JS::FinishIncrementalEncoding| with the mutable + // handle |aScript| argument of |CompileAndExec| or |JoinAndExec|. + ExecutionContext& SetEncodeBytecode(bool aEncodeBytecode) { + mEncodeBytecode = aEncodeBytecode; + return *this; + } + // Set the scope chain in which the code should be executed. void SetScopeChain(const JS::AutoObjectVector& aScopeChain); @@ -149,7 +161,8 @@ public: // Compile a script contained in a SourceBuffer, and execute it. nsresult CompileAndExec(JS::CompileOptions& aCompileOptions, - JS::SourceBufferHolder& aSrcBuf); + JS::SourceBufferHolder& aSrcBuf, + JS::MutableHandle aScript); // Compile a script contained in a string, and execute it. nsresult CompileAndExec(JS::CompileOptions& aCompileOptions, @@ -164,11 +177,6 @@ public: // function will get the result of the decoder by moving it to the main // thread before starting the execution of the script. MOZ_MUST_USE nsresult DecodeJoinAndExec(void **aOffThreadToken); - - // Similar to JoinAndExec, except that in addition to fecthing the source, - // we register the fact that we plan to encode its bytecode later. - MOZ_MUST_USE nsresult JoinEncodeAndExec(void **aOffThreadToken, - JS::MutableHandle aScript); }; static nsresult CompileModule(JSContext* aCx, diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index e3fc9e714b8d..3a8a75cb313b 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -760,7 +760,6 @@ support-files = file_js_cache.js file_js_cache_save_after_load.html file_js_cache_save_after_load.js -skip-if = (os == 'linux' || toolkit == 'android') # mochitest are executed on a single core [test_setInterval_uncatchable_exception.html] skip-if = debug == false [test_settimeout_extra_arguments.html] diff --git a/dom/base/test/test_script_loader_js_cache.html b/dom/base/test/test_script_loader_js_cache.html index 81ece7ee40a8..d81869587122 100644 --- a/dom/base/test/test_script_loader_js_cache.html +++ b/dom/base/test/test_script_loader_js_cache.html @@ -18,22 +18,26 @@ // code path, such that we can assert each code path with a single word. var scriptLoaderStateMachine = { "scriptloader_load_source": { - "scriptloader_encode_and_execute": { - "scriptloader_bytecode_saved": "bytecode_saved", - "scriptloader_bytecode_failed": "bytecode_failed" - }, - "scriptloader_execute": "source_exec" + "scriptloader_execute": { + "scriptloader_encode": { + "scriptloader_bytecode_saved": "bytecode_saved", + "scriptloader_bytecode_failed": "bytecode_failed" + }, + "scriptloader_no_encode": "source_exec" + } }, "scriptloader_load_bytecode": { "scriptloader_fallback": { // Replicate the top-level state machine without // "scriptloader_load_bytecode" transition. "scriptloader_load_source": { - "scriptloader_encode_and_execute": { - "scriptloader_bytecode_saved": "fallback_bytecode_saved", - "scriptloader_bytecode_failed": "fallback_bytecode_failed" - }, - "scriptloader_execute": "fallback_source_exec" + "scriptloader_execute": { + "scriptloader_encode": { + "scriptloader_bytecode_saved": "fallback_bytecode_saved", + "scriptloader_bytecode_failed": "fallback_bytecode_failed" + }, + "scriptloader_no_encode": "fallback_source_exec" + } } }, "scriptloader_execute": "bytecode_exec" @@ -93,7 +97,8 @@ iwin.addEventListener("scriptloader_load_bytecode", log_event); iwin.addEventListener("scriptloader_generate_bytecode", log_event); iwin.addEventListener("scriptloader_execute", log_event); - iwin.addEventListener("scriptloader_encode_and_execute", log_event); + iwin.addEventListener("scriptloader_encode", log_event); + iwin.addEventListener("scriptloader_no_encode", log_event); iwin.addEventListener("scriptloader_bytecode_saved", log_event); iwin.addEventListener("scriptloader_bytecode_failed", log_event); iwin.addEventListener("scriptloader_fallback", log_event); diff --git a/dom/media/eme/MediaKeySystemAccess.cpp b/dom/media/eme/MediaKeySystemAccess.cpp index 4773ab012a6a..6a149cac698b 100644 --- a/dom/media/eme/MediaKeySystemAccess.cpp +++ b/dom/media/eme/MediaKeySystemAccess.cpp @@ -667,8 +667,21 @@ GetSupportedCapabilities(const CodecType aCodecType, if (codecs.IsEmpty()) { // If container normatively implies a specific set of codecs and codec constraints: // Let parameters be that set. + if (isMP4) { + if (aCodecType == Audio) { + codecs.AppendElement(EME_CODEC_AAC); + } else if (aCodecType == Video) { + codecs.AppendElement(EME_CODEC_H264); + } + } else if (isWebM) { + if (aCodecType == Audio) { + codecs.AppendElement(EME_CODEC_VORBIS); + } else if (aCodecType == Video) { + codecs.AppendElement(EME_CODEC_VP8); + } + } // Otherwise: Continue to the next iteration. - continue; + // (Note: all containers we support have implied codecs, so don't continue here.) } // If container type is not strictly a audio/video type, continue to the next iteration. @@ -940,13 +953,8 @@ GetSupportedConfig(const KeySystemConfig& aKeySystem, // If the videoCapabilities and audioCapabilities members in candidate // configuration are both empty, return NotSupported. - if (aCandidate.mAudioCapabilities.IsEmpty() && - aCandidate.mVideoCapabilities.IsEmpty()) { - EME_LOG("MediaKeySystemConfiguration (label='%s') rejected; " - "no supported audio or video capabilities specified", - NS_ConvertUTF16toUTF8(aCandidate.mLabel).get()); - return false; - } + // TODO: Most sites using EME still don't pass capabilities, so we + // can't reject on it yet without breaking them. So add this later. // If the videoCapabilities member in candidate configuration is non-empty: if (!aCandidate.mVideoCapabilities.IsEmpty()) { diff --git a/dom/media/test/eme.js b/dom/media/test/eme.js index 1be161bddacd..3f9fec9caee2 100644 --- a/dom/media/test/eme.js +++ b/dom/media/test/eme.js @@ -2,8 +2,8 @@ const CLEARKEY_KEYSYSTEM = "org.w3.clearkey"; const gCencMediaKeySystemConfig = [{ initDataTypes: ['cenc'], - videoCapabilities: [{ contentType: 'video/mp4; codecs="avc1.42E01E"' }], - audioCapabilities: [{ contentType: 'audio/mp4; codecs="mp4a.40.2"' }], + videoCapabilities: [{ contentType: 'video/mp4' }], + audioCapabilities: [{ contentType: 'audio/mp4' }], }]; function IsMacOSSnowLeopardOrEarlier() { diff --git a/dom/media/test/test_eme_initDataTypes.html b/dom/media/test/test_eme_initDataTypes.html index 9d4f119c3398..4821c3cb82eb 100644 --- a/dom/media/test/test_eme_initDataTypes.html +++ b/dom/media/test/test_eme_initDataTypes.html @@ -94,7 +94,11 @@ function PrepareInitData(initDataType, initData) function Test(test) { return new Promise(function(resolve, reject) { - navigator.requestMediaKeySystemAccess('org.w3.clearkey', gCencMediaKeySystemConfig) + var configs = [{ + initDataTypes: [test.initDataType], + videoCapabilities: [{contentType: 'video/mp4' }], + }]; + navigator.requestMediaKeySystemAccess('org.w3.clearkey', configs) .then((access) => access.createMediaKeys()) .then((mediaKeys) => { var session = mediaKeys.createSession(test.sessionType); diff --git a/dom/media/test/test_eme_missing_pssh.html b/dom/media/test/test_eme_missing_pssh.html index afe959792ca2..196ced4022b1 100644 --- a/dom/media/test/test_eme_missing_pssh.html +++ b/dom/media/test/test_eme_missing_pssh.html @@ -35,8 +35,8 @@ function LoadEME() { var options = [{ - initDataTypes: ['cenc'], - audioCapabilities: [{ contentType: 'audio/mp4; codecs="mp4a.40.2"' }], + initDataType: 'cenc', + audioType: 'audio/mp4; codecs="mp4a.40.2"', }]; navigator.requestMediaKeySystemAccess("org.w3.clearkey", options) .then((keySystemAccess) => { diff --git a/dom/media/test/test_eme_requestKeySystemAccess.html b/dom/media/test/test_eme_requestKeySystemAccess.html index 5cb2912bac07..21ee91a68cdf 100644 --- a/dom/media/test/test_eme_requestKeySystemAccess.html +++ b/dom/media/test/test_eme_requestKeySystemAccess.html @@ -70,13 +70,6 @@ function Test(test) { }); } -const AUDIO_WEBM_VORBIS = 'audio/webm; codecs="vorbis"'; -const VIDEO_WEBM_VP8 = 'video/webm; codecs="vp8"'; -const VIDEO_WEBM_VP9 = 'video/webm; codecs="vp9"'; - -const AUDIO_MP4_AAC = 'audio/mp4; codecs="mp4a.40.2"'; -const VIDEO_MP4_H264 = 'video/mp4; codecs="avc1.42E01E"'; - var tests = [ { name: 'Empty keySystem string', @@ -84,7 +77,7 @@ var tests = [ options: [ { initDataTypes: ['cenc'], - videoCapabilities: [{contentType: VIDEO_MP4_H264}], + videoCapabilities: [{contentType: 'video/mp4'}], } ], shouldPass: false, @@ -104,15 +97,15 @@ var tests = [ { label: SUPPORTED_LABEL, initDataTypes: ['cenc'], - audioCapabilities: [{contentType: AUDIO_MP4_AAC}], - videoCapabilities: [{contentType: VIDEO_MP4_H264}], + audioCapabilities: [{contentType: 'audio/mp4'}], + videoCapabilities: [{contentType: 'video/mp4'}], } ], expectedConfig: { label: SUPPORTED_LABEL, initDataTypes: ['cenc'], - audioCapabilities: [{contentType: AUDIO_MP4_AAC}], - videoCapabilities: [{contentType: VIDEO_MP4_H264}], + audioCapabilities: [{contentType: 'audio/mp4'}], + videoCapabilities: [{contentType: 'video/mp4'}], }, shouldPass: true, }, @@ -122,7 +115,7 @@ var tests = [ options: [ { initDataTypes: ['cenc'], - videoCapabilities: [{contentType: VIDEO_MP4_H264}], + videoCapabilities: [{contentType: 'video/mp4'}], } ], shouldPass: false, @@ -132,7 +125,7 @@ var tests = [ options: [ { initDataTypes: ['bogus'], - audioCapabilities: [{contentType: AUDIO_MP4_AAC}], + audioCapabilities: [{contentType: 'audio/mp4'}], } ], shouldPass: false, @@ -143,13 +136,13 @@ var tests = [ { label: SUPPORTED_LABEL, initDataTypes: ['bogus', 'invalid', 'cenc'], - audioCapabilities: [{contentType: AUDIO_MP4_AAC}], + audioCapabilities: [{contentType: 'audio/mp4'}], } ], expectedConfig: { label: SUPPORTED_LABEL, initDataTypes: ['cenc'], - audioCapabilities: [{contentType: AUDIO_MP4_AAC}], + audioCapabilities: [{contentType: 'audio/mp4'}], }, shouldPass: true, }, @@ -168,7 +161,7 @@ var tests = [ options: [ { initDataTypes: ['cenc'], - videoCapabilities: [{contentType: VIDEO_MP4_H264}], + videoCapabilities: [{contentType: 'video/mp4'}], distinctiveIdentifier: 'bogus', persistentState: 'bogus', } @@ -180,7 +173,7 @@ var tests = [ options: [ { initDataTypes: ['cenc'], - videoCapabilities: [{contentType: VIDEO_MP4_H264}], + videoCapabilities: [{contentType: 'video/mp4'}], distinctiveIdentifier: 'required', } ], @@ -191,7 +184,7 @@ var tests = [ options: [ { initDataTypes: ['cenc'], - videoCapabilities: [{contentType: VIDEO_MP4_H264}], + videoCapabilities: [{contentType: 'video/mp4'}], persistentState: 'bogus', } ], @@ -202,7 +195,7 @@ var tests = [ options: [ { initDataTypes: ['cenc'], - videoCapabilities: [{contentType: VIDEO_MP4_H264, robustness: 'very much so'}], + videoCapabilities: [{contentType: 'video/mp4', robustness: 'very much so'}], } ], shouldPass: false, @@ -213,14 +206,14 @@ var tests = [ { label: SUPPORTED_LABEL, initDataTypes: ['cenc'], - videoCapabilities: [{contentType: VIDEO_MP4_H264}], + videoCapabilities: [{contentType: 'video/mp4'}], unexpectedEntry: 'this should be ignored', } ], expectedConfig: { label: SUPPORTED_LABEL, initDataTypes: ['cenc'], - videoCapabilities: [{contentType: VIDEO_MP4_H264}], + videoCapabilities: [{contentType: 'video/mp4'}], }, shouldPass: true, }, @@ -234,13 +227,13 @@ var tests = [ { label: SUPPORTED_LABEL, initDataTypes: ['cenc'], - videoCapabilities: [{contentType: VIDEO_MP4_H264}], + videoCapabilities: [{contentType: 'video/mp4'}], } ], expectedConfig: { label: SUPPORTED_LABEL, initDataTypes: ['cenc'], - videoCapabilities: [{contentType: VIDEO_MP4_H264}], + videoCapabilities: [{contentType: 'video/mp4'}], }, shouldPass: true, }, @@ -249,7 +242,7 @@ var tests = [ options: [ { initDataTypes: ['cenc'], - videoCapabilities: [{contentType: VIDEO_MP4_H264}], + videoCapabilities: [{contentType: 'video/mp4'}], sessionTypes: ['persistent-license'], persistentState: 'optional', } @@ -261,7 +254,7 @@ var tests = [ options: [ { initDataTypes: ['cenc'], - videoCapabilities: [{contentType: VIDEO_MP4_H264}], + videoCapabilities: [{contentType: 'video/mp4'}], sessionTypes: ['persistent-usage-record'], persistentState: 'optional', } @@ -274,13 +267,13 @@ var tests = [ { label: SUPPORTED_LABEL, initDataTypes: ['cenc'], - audioCapabilities: [{contentType: AUDIO_MP4_AAC}], + audioCapabilities: [{contentType: 'audio/mp4'}], } ], expectedConfig: { label: SUPPORTED_LABEL, initDataTypes: ['cenc'], - audioCapabilities: [{contentType: AUDIO_MP4_AAC}], + audioCapabilities: [{contentType: 'audio/mp4'}], }, shouldPass: true, }, @@ -290,13 +283,13 @@ var tests = [ { label: SUPPORTED_LABEL, initDataTypes: ['cenc'], - audioCapabilities: [{contentType: AUDIO_MP4_AAC}], + audioCapabilities: [{contentType: 'audio/mp4; codecs="mp4a.40.2"'}], } ], expectedConfig: { label: SUPPORTED_LABEL, initDataTypes: ['cenc'], - audioCapabilities: [{contentType: AUDIO_MP4_AAC}], + audioCapabilities: [{contentType: 'audio/mp4; codecs="mp4a.40.2"'}], }, shouldPass: true, }, @@ -346,13 +339,13 @@ var tests = [ { label: SUPPORTED_LABEL, initDataTypes: ['cenc'], - videoCapabilities: [{contentType: VIDEO_MP4_H264}], + videoCapabilities: [{contentType: 'video/mp4; codecs="avc1.42E01E"'}], } ], expectedConfig: { label: SUPPORTED_LABEL, initDataTypes: ['cenc'], - videoCapabilities: [{contentType: VIDEO_MP4_H264}], + videoCapabilities: [{contentType: 'video/mp4; codecs="avc1.42E01E"'}], }, shouldPass: true, }, @@ -382,15 +375,15 @@ var tests = [ { label: SUPPORTED_LABEL, initDataTypes: ['cenc'], - audioCapabilities: [{contentType: AUDIO_MP4_AAC}], - videoCapabilities: [{contentType: VIDEO_MP4_H264}], + videoCapabilities: [{contentType: 'video/mp4; codecs="avc1.42E01E"'}], + audioCapabilities: [{contentType: 'audio/mp4; codecs="mp4a.40.2"'}], } ], expectedConfig: { label: SUPPORTED_LABEL, initDataTypes: ['cenc'], - audioCapabilities: [{contentType: AUDIO_MP4_AAC}], - videoCapabilities: [{contentType: VIDEO_MP4_H264}], + videoCapabilities: [{contentType: 'video/mp4; codecs="avc1.42E01E"'}], + audioCapabilities: [{contentType: 'audio/mp4; codecs="mp4a.40.2"'}], }, shouldPass: true, }, @@ -400,13 +393,13 @@ var tests = [ { label: SUPPORTED_LABEL, initDataTypes: ['webm'], - videoCapabilities: [{contentType: VIDEO_WEBM_VP9}], + videoCapabilities: [{contentType: 'video/webm'}], } ], expectedConfig: { label: SUPPORTED_LABEL, initDataTypes: ['webm'], - videoCapabilities: [{contentType: VIDEO_WEBM_VP9}], + videoCapabilities: [{contentType: 'video/webm'}], }, shouldPass: true, }, @@ -416,13 +409,13 @@ var tests = [ { label: SUPPORTED_LABEL, initDataTypes: ['webm'], - audioCapabilities: [{contentType: AUDIO_WEBM_VORBIS}], + audioCapabilities: [{contentType: 'audio/webm'}], } ], expectedConfig: { label: SUPPORTED_LABEL, initDataTypes: ['webm'], - audioCapabilities: [{contentType: AUDIO_WEBM_VORBIS}], + audioCapabilities: [{contentType: 'audio/webm'}], }, shouldPass: true, }, @@ -432,15 +425,15 @@ var tests = [ { label: SUPPORTED_LABEL, initDataTypes: ['webm'], - videoCapabilities: [{contentType: VIDEO_WEBM_VP8}], - audioCapabilities: [{contentType: AUDIO_WEBM_VORBIS}], + videoCapabilities: [{contentType: 'video/webm;codecs="vp8"'}], + audioCapabilities: [{contentType: 'audio/webm;codecs="vorbis"'}], } ], expectedConfig: { label: SUPPORTED_LABEL, initDataTypes: ['webm'], - videoCapabilities: [{contentType: VIDEO_WEBM_VP8}], - audioCapabilities: [{contentType: AUDIO_WEBM_VORBIS}], + videoCapabilities: [{contentType: 'video/webm;codecs="vp8"'}], + audioCapabilities: [{contentType: 'audio/webm;codecs="vorbis"'}], }, shouldPass: true, }, @@ -450,15 +443,15 @@ var tests = [ { label: SUPPORTED_LABEL, initDataTypes: ['webm'], - videoCapabilities: [{contentType: VIDEO_WEBM_VP9}], - audioCapabilities: [{contentType: AUDIO_WEBM_VORBIS}], + videoCapabilities: [{contentType: 'video/webm;codecs="vp9"'}], + audioCapabilities: [{contentType: 'audio/webm;codecs="vorbis"'}], } ], expectedConfig: { label: SUPPORTED_LABEL, initDataTypes: ['webm'], - videoCapabilities: [{contentType: VIDEO_WEBM_VP9}], - audioCapabilities: [{contentType: AUDIO_WEBM_VORBIS}], + videoCapabilities: [{contentType: 'video/webm;codecs="vp9"'}], + audioCapabilities: [{contentType: 'audio/webm;codecs="vorbis"'}], }, shouldPass: true, }, @@ -472,15 +465,6 @@ var tests = [ ], shouldPass: false, }, - { - name: 'Unspecified capabilities', - options: [ - { - initDataTypes: ['cenc'], - } - ], - shouldPass: false, - }, ]; function beginTest() { diff --git a/dom/media/test/test_eme_sample_groups_playback.html b/dom/media/test/test_eme_sample_groups_playback.html index 46d5cbf11f54..77aa59f07f7f 100644 --- a/dom/media/test/test_eme_sample_groups_playback.html +++ b/dom/media/test/test_eme_sample_groups_playback.html @@ -58,8 +58,8 @@ function LoadEME() { var options = [{ - initDataTypes: ['cenc'], - videoCapabilities: [{contentType: test.track.type}], + initDataType: "cenc", + videoType: test.track.type, }]; return navigator.requestMediaKeySystemAccess("org.w3.clearkey", options) @@ -143,4 +143,4 @@ - + \ No newline at end of file diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp index 4f71c6f28d0c..06bd1207292a 100644 --- a/dom/script/ScriptLoader.cpp +++ b/dom/script/ScriptLoader.cpp @@ -1879,13 +1879,6 @@ ScriptLoader::FillCompileOptionsForRequest(const AutoJSAPI&jsapi, aOptions->setElement(&elementVal.toObject()); } - // At the moment, the bytecode cache is only triggered if a script is large - // enough to be parsed out of the main thread. Thus, for testing purposes, we - // force parsing any script out of the main thread. - if (IsEagerBytecodeCache()) { - aOptions->forceAsync = true; - } - return NS_OK; } @@ -1993,49 +1986,46 @@ ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) MOZ_ASSERT(!aRequest->mCacheInfo); } else { MOZ_ASSERT(aRequest->IsSource()); - if (aRequest->mOffThreadToken) { - // Off-main-thread parsing. - LOG(("ScriptLoadRequest (%p): Join (off-thread parsing) and Execute", - aRequest)); - { - nsJSUtils::ExecutionContext exec(aes.cx(), global); - JS::Rooted script(aes.cx()); - if (!aRequest->mCacheInfo) { - TRACE_FOR_TEST(aRequest->mElement, "scriptloader_execute"); - rv = exec.JoinAndExec(&aRequest->mOffThreadToken, &script); - LOG(("ScriptLoadRequest (%p): Cannot cache anything (cacheInfo = nullptr)", - aRequest)); - } else { - TRACE_FOR_TEST(aRequest->mElement, "scriptloader_encode_and_execute"); - MOZ_ASSERT(aRequest->mBytecodeOffset == - aRequest->mScriptBytecode.length()); - rv = exec.JoinEncodeAndExec(&aRequest->mOffThreadToken, - &script); - // Queue the current script load request to later save the bytecode. - if (NS_SUCCEEDED(rv)) { - aRequest->mScript = script; - HoldJSObjects(aRequest); - RegisterForBytecodeEncoding(aRequest); - } else { - LOG(("ScriptLoadRequest (%p): Cannot cache anything (rv = %X, script = %p, cacheInfo = %p)", - aRequest, unsigned(rv), script.get(), aRequest->mCacheInfo.get())); - TRACE_FOR_TEST_NONE(aRequest->mElement, "scriptloader_bytecode_failed"); - aRequest->mCacheInfo = nullptr; - } - } - } - } else { - // Main thread parsing (inline and small scripts) - LOG(("ScriptLoadRequest (%p): Compile And Exec", aRequest)); + JS::Rooted script(aes.cx()); + + bool encodeBytecode = false; + if (aRequest->mCacheInfo) { + MOZ_ASSERT(aRequest->mBytecodeOffset == + aRequest->mScriptBytecode.length()); + encodeBytecode = IsEagerBytecodeCache(); // Heuristic! + } + + { nsJSUtils::ExecutionContext exec(aes.cx(), global); - nsAutoString inlineData; - SourceBufferHolder srcBuf = GetScriptSource(aRequest, inlineData); + exec.SetEncodeBytecode(encodeBytecode); TRACE_FOR_TEST(aRequest->mElement, "scriptloader_execute"); - rv = exec.CompileAndExec(options, srcBuf); + if (aRequest->mOffThreadToken) { + // Off-main-thread parsing. + LOG(("ScriptLoadRequest (%p): Join (off-thread parsing) and Execute", + aRequest)); + rv = exec.JoinAndExec(&aRequest->mOffThreadToken, &script); + } else { + // Main thread parsing (inline and small scripts) + LOG(("ScriptLoadRequest (%p): Compile And Exec", aRequest)); + nsAutoString inlineData; + SourceBufferHolder srcBuf = GetScriptSource(aRequest, inlineData); + rv = exec.CompileAndExec(options, srcBuf, &script); + } + } + + // Queue the current script load request to later save the bytecode. + if (NS_SUCCEEDED(rv) && encodeBytecode) { + aRequest->mScript = script; + HoldJSObjects(aRequest); + TRACE_FOR_TEST(aRequest->mElement, "scriptloader_encode"); + RegisterForBytecodeEncoding(aRequest); + } else { + LOG(("ScriptLoadRequest (%p): Cannot cache anything (rv = %X, script = %p, cacheInfo = %p)", + aRequest, unsigned(rv), script.get(), aRequest->mCacheInfo.get())); + TRACE_FOR_TEST_NONE(aRequest->mElement, "scriptloader_no_encode"); aRequest->mCacheInfo = nullptr; } } - } } diff --git a/gfx/thebes/gfxASurface.cpp b/gfx/thebes/gfxASurface.cpp index 047527d12c87..eed5c94bea11 100644 --- a/gfx/thebes/gfxASurface.cpp +++ b/gfx/thebes/gfxASurface.cpp @@ -472,7 +472,10 @@ public: sSurfaceMemoryUsed[size_t(aType)] = sSurfaceMemoryUsed[size_t(aType)] + aBytes; }; - NS_DECL_ISUPPORTS + // This memory reporter is sometimes allocated on the compositor thread, + // but always released on the main thread, so its refcounting needs to be + // threadsafe. + NS_DECL_THREADSAFE_ISUPPORTS NS_IMETHOD CollectReports(nsIHandleReportCallback *aHandleReport, nsISupports *aData, bool aAnonymize) override diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index 6f1cb7285853..14a08bbef3f7 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -2604,13 +2604,8 @@ gfxFont::GetShapedWord(DrawTarget *aDrawTarget, } gfxShapedWord* sw = entry->mShapedWord.get(); - bool isContent = !mStyle.systemFont; - if (sw) { sw->ResetAge(); - Telemetry::Accumulate((isContent ? Telemetry::WORD_CACHE_HITS_CONTENT : - Telemetry::WORD_CACHE_HITS_CHROME), - aLength); #ifndef RELEASE_OR_BETA if (aTextPerf) { aTextPerf->current.wordCacheHit++; @@ -2619,9 +2614,6 @@ gfxFont::GetShapedWord(DrawTarget *aDrawTarget, return sw; } - Telemetry::Accumulate((isContent ? Telemetry::WORD_CACHE_MISSES_CONTENT : - Telemetry::WORD_CACHE_MISSES_CHROME), - aLength); #ifndef RELEASE_OR_BETA if (aTextPerf) { aTextPerf->current.wordCacheMiss++; diff --git a/js/src/jit-test/tests/basic/bug1355573.js b/js/src/jit-test/tests/basic/bug1355573.js new file mode 100644 index 000000000000..58ff6d63461d --- /dev/null +++ b/js/src/jit-test/tests/basic/bug1355573.js @@ -0,0 +1,6 @@ +// |jit-test| error:overflow +if (getBuildConfiguration().debug === true) + throw "overflow"; +function f(){}; +Object.defineProperty(f, "name", {value: "a".repeat((1<<28)-1)}); +len = f.bind().name.length; diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp index cf041376d6c3..774db24cbdc9 100644 --- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -345,6 +345,11 @@ AtomizeAndCopyChars(JSContext* cx, const CharT* tbchars, size_t length, PinningB } } + // Validate the length before taking the exclusive access lock, as throwing + // an exception here may reenter this code. + if (MOZ_UNLIKELY(!JSString::validateLength(cx, length))) + return nullptr; + AutoLockForExclusiveAccess lock(cx); AtomSet& atoms = cx->atoms(lock); @@ -358,9 +363,6 @@ AtomizeAndCopyChars(JSContext* cx, const CharT* tbchars, size_t length, PinningB return atom; } - if (!JSString::validateLength(cx, length)) - return nullptr; - JSAtom* atom; { AutoAtomsCompartment ac(cx, lock); diff --git a/layout/style/ServoBindingList.h b/layout/style/ServoBindingList.h index 2fb4593c7a53..8041319c8206 100644 --- a/layout/style/ServoBindingList.h +++ b/layout/style/ServoBindingList.h @@ -47,6 +47,8 @@ SERVO_BINDING_FUNC(Servo_StyleSheet_GetRules, ServoCssRulesStrong, RawServoStyleSheetBorrowed sheet) SERVO_BINDING_FUNC(Servo_StyleSheet_Clone, RawServoStyleSheetStrong, RawServoStyleSheetBorrowed sheet) +SERVO_BINDING_FUNC(Servo_StyleSheet_SizeOfIncludingThis, size_t, + mozilla::MallocSizeOf malloc_size_of, RawServoStyleSheetBorrowed sheet) SERVO_BINDING_FUNC(Servo_StyleSet_Init, RawServoStyleSetOwned, RawGeckoPresContextOwned pres_context) SERVO_BINDING_FUNC(Servo_StyleSet_Clear, void, RawServoStyleSetBorrowed set) diff --git a/layout/style/ServoBindings.toml b/layout/style/ServoBindings.toml index a278603c04c4..6108a34200c8 100644 --- a/layout/style/ServoBindings.toml +++ b/layout/style/ServoBindings.toml @@ -321,6 +321,7 @@ structs-types = [ "mozilla::css::GridTemplateAreasValue", "mozilla::css::ImageValue", "mozilla::css::URLValue", + "mozilla::MallocSizeOf", "mozilla::Side", "RawGeckoAnimationPropertySegment", "RawGeckoComputedTiming", diff --git a/layout/style/ServoStyleSheet.cpp b/layout/style/ServoStyleSheet.cpp index 56e362cde88e..fda4c2ec4c25 100644 --- a/layout/style/ServoStyleSheet.cpp +++ b/layout/style/ServoStyleSheet.cpp @@ -59,13 +59,13 @@ ServoStyleSheetInner::CloneFor(StyleSheet* aPrimarySheet) static_cast(aPrimarySheet)); } +MOZ_DEFINE_MALLOC_SIZE_OF(ServoStyleSheetMallocSizeOf) + size_t ServoStyleSheetInner::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { size_t n = aMallocSizeOf(this); - - // XXX: need to measure mSheet - + n += Servo_StyleSheet_SizeOfIncludingThis(ServoStyleSheetMallocSizeOf, mSheet); return n; } diff --git a/layout/svg/crashtests/crashtests.list b/layout/svg/crashtests/crashtests.list index 9d2d75c55f97..600e17962f97 100644 --- a/layout/svg/crashtests/crashtests.list +++ b/layout/svg/crashtests/crashtests.list @@ -105,7 +105,7 @@ load 587336-1.html load 590291-1.svg load 601999-1.html load 605626-1.svg -asserts(2) load 606914.xhtml # bug 606914, bug 1340561 tracks the stylo bit +asserts(2) load 606914.xhtml # bug 606914 load 610594-1.html load 610954-1.html load 612662-1.svg diff --git a/layout/xul/test/browser.ini b/layout/xul/test/browser.ini index 3fa28b15364c..ef38031f139b 100644 --- a/layout/xul/test/browser.ini +++ b/layout/xul/test/browser.ini @@ -3,6 +3,6 @@ [browser_bug685470.js] [browser_bug703210.js] [browser_bug706743.js] -skip-if = (os == 'linux') # Bug 1157576 +skip-if = (os == 'linux') || e10s # Bug 1157576 [browser_bug1163304.js] skip-if = os != 'linux' && os != 'win' // Due to testing menubar behavior with keyboard diff --git a/media/libstagefright/moz.build b/media/libstagefright/moz.build index af5d1194d621..65cd0ff8f033 100644 --- a/media/libstagefright/moz.build +++ b/media/libstagefright/moz.build @@ -10,7 +10,7 @@ with Files("**"): DEFINES['ANDROID_SMP'] = 0 DEFINES['LOG_NDEBUG'] = 1 -if CONFIG['OS_TARGET'] != 'WINNT': +if CONFIG['OS_TARGET'] != 'WINNT' and CONFIG['OS_TARGET'] != 'SunOS': DEFINES['_GLIBCXX_OS_DEFINES'] = True if CONFIG['OS_TARGET'] == 'WINNT': diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 705bbd1cb961..d8186b8c106c 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -4843,6 +4843,8 @@ pref("extensions.webextensions.themes.enabled", false); pref("extensions.webextensions.themes.icons.enabled", false); pref("extensions.webextensions.remote", false); +pref("layers.popups.compositing.enabled", false); + // Report Site Issue button pref("extensions.webcompat-reporter.newIssueEndpoint", "https://webcompat.com/issues/new"); #ifdef NIGHTLY_BUILD diff --git a/netwerk/cookie/CookieServiceChild.cpp b/netwerk/cookie/CookieServiceChild.cpp index ba9879976f84..842fc96cd9df 100644 --- a/netwerk/cookie/CookieServiceChild.cpp +++ b/netwerk/cookie/CookieServiceChild.cpp @@ -6,6 +6,7 @@ #include "mozilla/net/CookieServiceChild.h" #include "mozilla/LoadInfo.h" #include "mozilla/BasePrincipal.h" +#include "mozilla/dom/ContentChild.h" #include "mozilla/ipc/URIUtils.h" #include "mozilla/net/NeckoChild.h" #include "nsIChannel.h" @@ -47,6 +48,12 @@ CookieServiceChild::CookieServiceChild() { NS_ASSERTION(IsNeckoChild(), "not a child process"); + mozilla::dom::ContentChild* cc = + static_cast(gNeckoChild->Manager()); + if (cc->IsShuttingDown()) { + return; + } + // This corresponds to Release() in DeallocPCookieService. NS_ADDREF_THIS(); diff --git a/netwerk/dns/DNSRequestChild.cpp b/netwerk/dns/DNSRequestChild.cpp index 03f60f1d4bb9..e26078cdca59 100644 --- a/netwerk/dns/DNSRequestChild.cpp +++ b/netwerk/dns/DNSRequestChild.cpp @@ -4,6 +4,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "mozilla/dom/ContentChild.h" #include "mozilla/net/ChildDNSService.h" #include "mozilla/net/DNSRequestChild.h" #include "mozilla/net/NeckoChild.h" @@ -223,6 +224,12 @@ DNSRequestChild::StartRequest() gNeckoChild->SetEventTargetForActor(this, systemGroupEventTarget); + mozilla::dom::ContentChild* cc = + static_cast(gNeckoChild->Manager()); + if (cc->IsShuttingDown()) { + return; + } + // Send request to Parent process. gNeckoChild->SendPDNSRequestConstructor(this, mHost, mOriginAttributes, mFlags, mNetworkInterface); diff --git a/netwerk/ipc/NeckoChild.cpp b/netwerk/ipc/NeckoChild.cpp index 78b51b455277..289f625f4e08 100644 --- a/netwerk/ipc/NeckoChild.cpp +++ b/netwerk/ipc/NeckoChild.cpp @@ -66,6 +66,9 @@ void NeckoChild::InitNeckoChild() mozilla::dom::ContentChild * cpc = mozilla::dom::ContentChild::GetSingleton(); NS_ASSERTION(cpc, "Content Protocol is NULL!"); + if (NS_WARN_IF(cpc->IsShuttingDown())) { + return; + } gNeckoChild = cpc->SendPNeckoConstructor(); NS_ASSERTION(gNeckoChild, "PNecko Protocol init failed!"); } diff --git a/netwerk/protocol/data/DataChannelChild.cpp b/netwerk/protocol/data/DataChannelChild.cpp index 137eb74b6fd2..e43040167adc 100644 --- a/netwerk/protocol/data/DataChannelChild.cpp +++ b/netwerk/protocol/data/DataChannelChild.cpp @@ -7,6 +7,7 @@ #include "DataChannelChild.h" #include "mozilla/Unused.h" +#include "mozilla/dom/ContentChild.h" #include "mozilla/net/NeckoChild.h" namespace mozilla { @@ -27,6 +28,12 @@ DataChannelChild::~DataChannelChild() NS_IMETHODIMP DataChannelChild::ConnectParent(uint32_t aId) { + mozilla::dom::ContentChild* cc = + static_cast(gNeckoChild->Manager()); + if (cc->IsShuttingDown()) { + return NS_ERROR_FAILURE; + } + if (!gNeckoChild->SendPDataChannelConstructor(this, aId)) { return NS_ERROR_FAILURE; } diff --git a/netwerk/protocol/file/FileChannelChild.cpp b/netwerk/protocol/file/FileChannelChild.cpp index 57f79fcd05c3..15cc61cb88cb 100644 --- a/netwerk/protocol/file/FileChannelChild.cpp +++ b/netwerk/protocol/file/FileChannelChild.cpp @@ -7,6 +7,7 @@ #include "FileChannelChild.h" #include "mozilla/Unused.h" +#include "mozilla/dom/ContentChild.h" #include "mozilla/net/NeckoChild.h" namespace mozilla { @@ -23,6 +24,12 @@ FileChannelChild::FileChannelChild(nsIURI *uri) NS_IMETHODIMP FileChannelChild::ConnectParent(uint32_t id) { + mozilla::dom::ContentChild* cc = + static_cast(gNeckoChild->Manager()); + if (cc->IsShuttingDown()) { + return NS_ERROR_FAILURE; + } + if (!gNeckoChild->SendPFileChannelConstructor(this, id)) { return NS_ERROR_FAILURE; } diff --git a/netwerk/protocol/ftp/FTPChannelChild.cpp b/netwerk/protocol/ftp/FTPChannelChild.cpp index a0fe13ff0e2c..d69bd0f3d5d1 100644 --- a/netwerk/protocol/ftp/FTPChannelChild.cpp +++ b/netwerk/protocol/ftp/FTPChannelChild.cpp @@ -24,6 +24,7 @@ #include "mozilla/ipc/BackgroundUtils.h" #include "nsIPrompt.h" +using mozilla::dom::ContentChild; using namespace mozilla::ipc; #undef LOG @@ -153,6 +154,8 @@ FTPChannelChild::AsyncOpen(::nsIStreamListener* listener, nsISupports* aContext) LOG(("FTPChannelChild::AsyncOpen [this=%p]\n", this)); NS_ENSURE_TRUE((gNeckoChild), NS_ERROR_FAILURE); + NS_ENSURE_TRUE(!static_cast(gNeckoChild->Manager())-> + IsShuttingDown(), NS_ERROR_FAILURE); NS_ENSURE_ARG_POINTER(listener); NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); @@ -188,7 +191,7 @@ FTPChannelChild::AsyncOpen(::nsIStreamListener* listener, nsISupports* aContext) mozilla::ipc::AutoIPCStream autoStream; autoStream.Serialize(mUploadStream, - static_cast(gNeckoChild->Manager())); + static_cast(gNeckoChild->Manager())); FTPChannelOpenArgs openArgs; SerializeURI(nsBaseChannel::URI(), openArgs.uri()); @@ -873,6 +876,10 @@ FTPChannelChild::Resume() NS_IMETHODIMP FTPChannelChild::ConnectParent(uint32_t id) { + NS_ENSURE_TRUE((gNeckoChild), NS_ERROR_FAILURE); + NS_ENSURE_TRUE(!static_cast(gNeckoChild->Manager())-> + IsShuttingDown(), NS_ERROR_FAILURE); + LOG(("FTPChannelChild::ConnectParent [this=%p]\n", this)); mozilla::dom::TabChild* tabChild = nullptr; @@ -936,6 +943,8 @@ FTPChannelChild::DivertToParent(ChannelDiverterChild **aChild) MOZ_RELEASE_ASSERT(aChild); MOZ_RELEASE_ASSERT(gNeckoChild); MOZ_RELEASE_ASSERT(!mDivertingToParent); + NS_ENSURE_TRUE(!static_cast(gNeckoChild->Manager())-> + IsShuttingDown(), NS_ERROR_FAILURE); LOG(("FTPChannelChild::DivertToParent [this=%p]\n", this)); diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp index 709406205fa3..9a2a3f86023d 100644 --- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -1823,6 +1823,11 @@ HttpChannelChild::ConnectParent(uint32_t registrarId) return NS_ERROR_FAILURE; } + ContentChild* cc = static_cast(gNeckoChild->Manager()); + if (cc->IsShuttingDown()) { + return NS_ERROR_FAILURE; + } + HttpBaseChannel::SetDocshellUserAgentOverride(); // The socket transport in the chrome process now holds a logical ref to us @@ -2665,6 +2670,9 @@ HttpChannelChild::OpenAlternativeOutputStream(const nsACString & aType, nsIOutpu if (!mIPCOpen) { return NS_ERROR_NOT_AVAILABLE; } + if (static_cast(gNeckoChild->Manager())->IsShuttingDown()) { + return NS_ERROR_NOT_AVAILABLE; + } RefPtr stream = static_cast(gNeckoChild->SendPAltDataOutputStreamConstructor(nsCString(aType), this)); @@ -2996,6 +3004,9 @@ HttpChannelChild::DivertToParent(ChannelDiverterChild **aChild) if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + if (static_cast(gNeckoChild->Manager())->IsShuttingDown()) { + return NS_ERROR_FAILURE; + } HttpChannelDiverterArgs args; args.mChannelChild() = this; diff --git a/security/nss/TAG-INFO b/security/nss/TAG-INFO index eb00f63614c7..08c8b315f419 100644 --- a/security/nss/TAG-INFO +++ b/security/nss/TAG-INFO @@ -1 +1 @@ -57e38a8407b3 +29290a4a9bd0 diff --git a/security/nss/automation/abi-check/expected-report-libfreebl3.so.txt b/security/nss/automation/abi-check/expected-report-libfreebl3.so.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/security/nss/automation/abi-check/expected-report-libfreeblpriv3.so.txt b/security/nss/automation/abi-check/expected-report-libfreeblpriv3.so.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/security/nss/automation/abi-check/expected-report-libnspr4.so.txt b/security/nss/automation/abi-check/expected-report-libnspr4.so.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/security/nss/automation/abi-check/expected-report-libnss3.so.txt b/security/nss/automation/abi-check/expected-report-libnss3.so.txt new file mode 100644 index 000000000000..1bdbb409097b --- /dev/null +++ b/security/nss/automation/abi-check/expected-report-libnss3.so.txt @@ -0,0 +1,12 @@ +Functions changes summary: 0 Removed, 0 Changed, 6 Added functions +Variables changes summary: 0 Removed, 0 Changed, 0 Added variable + +6 Added functions: + + 'function SECStatus CERT_GetCertIsPerm(const CERTCertificate*, PRBool*)' {CERT_GetCertIsPerm@@NSS_3.31} + 'function SECStatus CERT_GetCertIsTemp(const CERTCertificate*, PRBool*)' {CERT_GetCertIsTemp@@NSS_3.31} + 'function CERTCertificate* PK11_FindCertFromURI(const char*, void*)' {PK11_FindCertFromURI@@NSS_3.31} + 'function CERTCertList* PK11_FindCertsFromURI(const char*, void*)' {PK11_FindCertsFromURI@@NSS_3.31} + 'function char* PK11_GetModuleURI(SECMODModule*)' {PK11_GetModuleURI@@NSS_3.31} + 'function char* PK11_GetTokenURI()' {PK11_GetTokenURI@@NSS_3.31} + diff --git a/security/nss/automation/abi-check/expected-report-libnssckbi.so.txt b/security/nss/automation/abi-check/expected-report-libnssckbi.so.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/security/nss/automation/abi-check/expected-report-libnssdbm3.so.txt b/security/nss/automation/abi-check/expected-report-libnssdbm3.so.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/security/nss/automation/abi-check/expected-report-libnsssysinit.so.txt b/security/nss/automation/abi-check/expected-report-libnsssysinit.so.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/security/nss/automation/abi-check/expected-report-libnssutil3.so.txt b/security/nss/automation/abi-check/expected-report-libnssutil3.so.txt new file mode 100644 index 000000000000..5c6e5d9e51f7 --- /dev/null +++ b/security/nss/automation/abi-check/expected-report-libnssutil3.so.txt @@ -0,0 +1,12 @@ +Functions changes summary: 0 Removed, 0 Changed, 6 Added functions +Variables changes summary: 0 Removed, 0 Changed, 0 Added variable + +6 Added functions: + + 'function void PK11URI_CreateURI(size_t, size_t)' {PK11URI_CreateURI@@NSSUTIL_3.31} + 'function void PK11URI_DestroyURI()' {PK11URI_DestroyURI@@NSSUTIL_3.31} + 'function char* PK11URI_FormatURI()' {PK11URI_FormatURI@@NSSUTIL_3.31} + 'function const char* PK11URI_GetPathAttribute(const char*)' {PK11URI_GetPathAttribute@@NSSUTIL_3.31} + 'function const char* PK11URI_GetQueryAttribute(const char*)' {PK11URI_GetQueryAttribute@@NSSUTIL_3.31} + 'function void PK11URI_ParseURI(const char*)' {PK11URI_ParseURI@@NSSUTIL_3.31} + diff --git a/security/nss/automation/abi-check/expected-report-libplc4.so.txt b/security/nss/automation/abi-check/expected-report-libplc4.so.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/security/nss/automation/abi-check/expected-report-libplds4.so.txt b/security/nss/automation/abi-check/expected-report-libplds4.so.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/security/nss/automation/abi-check/expected-report-libsmime3.so.txt b/security/nss/automation/abi-check/expected-report-libsmime3.so.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/security/nss/automation/abi-check/expected-report-libsoftokn3.so.txt b/security/nss/automation/abi-check/expected-report-libsoftokn3.so.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/security/nss/automation/abi-check/expected-report-libssl3.so.txt b/security/nss/automation/abi-check/expected-report-libssl3.so.txt new file mode 100644 index 000000000000..b0a279d9cf98 --- /dev/null +++ b/security/nss/automation/abi-check/expected-report-libssl3.so.txt @@ -0,0 +1,14 @@ +Functions changes summary: 0 Removed, 1 Changed, 0 Added function +Variables changes summary: 0 Removed, 0 Changed, 0 Added variable + +1 function with some indirect sub-type change: + + [C]'function SECStatus SSL_GetPreliminaryChannelInfo(SSLPreliminaryChannelInfo*, PRUintn)' at sslinfo.c:115:1 has some indirect sub-type changes: + parameter 1 of type 'SSLPreliminaryChannelInfo*' has sub-type changes: + in pointed to type 'typedef SSLPreliminaryChannelInfo' at sslt.h:318:1: + underlying type 'struct SSLPreliminaryChannelInfoStr' at sslt.h:287:1 changed: + type size changed from 128 to 160 bits + 1 data member insertion: + 'PRUint32 SSLPreliminaryChannelInfoStr::maxEarlyDataSize', at offset 128 (in bits) at sslt.h:314:1 + + diff --git a/security/nss/automation/abi-check/previous-nss-release b/security/nss/automation/abi-check/previous-nss-release new file mode 100644 index 000000000000..1445e74b1a3c --- /dev/null +++ b/security/nss/automation/abi-check/previous-nss-release @@ -0,0 +1 @@ +NSS_3_30_BRANCH diff --git a/security/nss/automation/buildbot-slave/build.sh b/security/nss/automation/buildbot-slave/build.sh index 7f1a960be78a..3fc914803ffd 100755 --- a/security/nss/automation/buildbot-slave/build.sh +++ b/security/nss/automation/buildbot-slave/build.sh @@ -19,6 +19,9 @@ proc_args() "--test-nss") TEST_NSS=1 ;; + "--check-abi") + CHECK_ABI=1 + ;; "--build-jss") BUILD_JSS=1 ;; @@ -40,6 +43,7 @@ proc_args() echo " --build-jss" echo " --test-nss" echo " --test-jss" + echo " --check-abi" exit 1 ;; esac @@ -215,6 +219,65 @@ test_nss() return ${RET} } +check_abi() +{ + print_log "######## NSS ABI CHECK - ${BITS} bits - ${OPT} ########" + print_log "######## creating temporary HG clones ########" + + rm -rf ${HGDIR}/baseline + mkdir ${HGDIR}/baseline + BASE_NSS=`cat ${HGDIR}/nss/automation/abi-check/previous-nss-release` + hg clone -u "${BASE_NSS}" "${HGDIR}/nss" "${HGDIR}/baseline/nss" + if [ $? -ne 0 ]; then + echo "invalid tag in automation/abi-check/previous-nss-release" + return 1 + fi + + BASE_NSPR=NSPR_$(head -1 ${HGDIR}/baseline/nss/automation/release/nspr-version.txt | cut -d . -f 1-2 | tr . _)_BRANCH + hg clone -u "${BASE_NSPR}" "${HGDIR}/nspr" "${HGDIR}/baseline/nspr" + if [ $? -ne 0 ]; then + echo "invalid tag ${BASE_NSPR} derived from ${BASE_NSS} automation/release/nspr-version.txt" + return 1 + fi + + print_log "######## building older NSPR/NSS ########" + pushd ${HGDIR}/baseline/nss + + print_log "$ ${MAKE} ${NSS_BUILD_TARGET}" + ${MAKE} ${NSS_BUILD_TARGET} 2>&1 | tee -a ${LOG_ALL} + RET=$? + print_result "NSS - build - ${BITS} bits - ${OPT}" ${RET} 0 + if [ ${RET} -ne 0 ]; then + tail -100 ${LOG_ALL} + return ${RET} + fi + popd + + ABI_REPORT=${OUTPUTDIR}/abi-diff.txt + rm -f ${ABI_REPORT} + PREVDIST=${HGDIR}/baseline/dist + NEWDIST=${HGDIR}/dist + ALL_SOs="libfreebl3.so libfreeblpriv3.so libnspr4.so libnss3.so libnssckbi.so libnssdbm3.so libnsssysinit.so libnssutil3.so libplc4.so libplds4.so libsmime3.so libsoftokn3.so libssl3.so" + for SO in ${ALL_SOs}; do + if [ ! -f nss/automation/abi-check/expected-report-$SO.txt ]; then + touch nss/automation/abi-check/expected-report-$SO.txt + fi + abidiff --hd1 $PREVDIST/public/ --hd2 $NEWDIST/public \ + $PREVDIST/*/lib/$SO $NEWDIST/*/lib/$SO \ + > nss/automation/abi-check/new-report-$SO.txt + diff -u nss/automation/abi-check/expected-report-$SO.txt \ + nss/automation/abi-check/new-report-$SO.txt >> ${ABI_REPORT} + done + + if [ -s ${ABI_REPORT} ]; then + print_log "FAILED: there are new unexpected ABI changes" + cat ${ABI_REPORT} + return 1 + fi + + return 0 +} + test_jss() { print_log "######## JSS - tests - ${BITS} bits - ${OPT} ########" @@ -288,6 +351,11 @@ build_and_test() [ $? -eq 0 ] || return 1 fi + if [ -n "${CHECK_ABI}" ]; then + check_abi + [ $? -eq 0 ] || return 1 + fi + if [ -n "${BUILD_JSS}" ]; then create_objdir_dist_link build_jss @@ -360,6 +428,7 @@ main() { VALID=0 RET=1 + FAIL=0 for BITS in 32 64; do echo ${RUN_BITS} | grep ${BITS} > /dev/null @@ -372,7 +441,10 @@ main() set_env run_all RET=$? - print_log "### result of run_all is ${RET}" + print_log "### result of run_all is ${RET}" + if [ ${RET} -ne 0 ]; then + FAIL=${RET} + fi done done @@ -381,7 +453,7 @@ main() return 1 fi - return ${RET} + return ${FAIL} } #function killallsub() @@ -409,6 +481,8 @@ echo "tinderbox args: $0 $@" proc_args "$@" main -#RET=$? +RET=$? +print_log "### result of main is ${RET}" + #rm $IS_RUNNING_FILE -#exit ${RET} +exit ${RET} diff --git a/security/nss/automation/release/nspr-version.txt b/security/nss/automation/release/nspr-version.txt new file mode 100644 index 000000000000..9a4d1308957e --- /dev/null +++ b/security/nss/automation/release/nspr-version.txt @@ -0,0 +1,10 @@ +4.15 + +# The first line of this file must contain the human readable NSPR +# version number, which is the minimum required version of NSPR +# that is supported by this version of NSS. +# +# This information is used by release automation, +# when creating an NSS source archive. +# +# All other lines in this file are ignored. diff --git a/security/nss/automation/release/nss-release-helper.py b/security/nss/automation/release/nss-release-helper.py index a7b408828104..d168febde6af 100755 --- a/security/nss/automation/release/nss-release-helper.py +++ b/security/nss/automation/release/nss-release-helper.py @@ -171,11 +171,13 @@ def set_4_digit_release_number(): set_all_lib_versions(version, major, minor, patch, build) def create_nss_release_archive(): - ensure_arguments_after_action(4, "nss_release_version nss_hg_release_tag nspr_release_version path_to_stage_directory") + ensure_arguments_after_action(3, "nss_release_version nss_hg_release_tag path_to_stage_directory") nssrel = args[1].strip() #e.g. 3.19.3 nssreltag = args[2].strip() #e.g. NSS_3_19_3_RTM - nsprrel = args[3].strip() #e.g. 4.10.8 - stagedir = args[4].strip() #e.g. ../stage + stagedir = args[3].strip() #e.g. ../stage + + with open('automation/release/nspr-version.txt') as nspr_version_file: + nsprrel = next(nspr_version_file).strip() nspr_tar = "nspr-" + nsprrel + ".tar.gz" nsprtar_with_path= stagedir + "/v" + nsprrel + "/src/" + nspr_tar diff --git a/security/nss/automation/taskcluster/docker-fuzz/setup.sh b/security/nss/automation/taskcluster/docker-fuzz/setup.sh index a5a36f674e04..fcb72346e9d8 100644 --- a/security/nss/automation/taskcluster/docker-fuzz/setup.sh +++ b/security/nss/automation/taskcluster/docker-fuzz/setup.sh @@ -22,6 +22,10 @@ apt_packages+=('ninja-build') apt_packages+=('pkg-config') apt_packages+=('zlib1g-dev') +# 32-bit builds +apt_packages+=('gcc-multilib') +apt_packages+=('g++-multilib') + # Latest Mercurial. apt_packages+=('mercurial') apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 41BD8711B1F0EC2B0D85B91CF59CE3A8323293EE @@ -31,6 +35,11 @@ echo "deb http://ppa.launchpad.net/mercurial-ppa/releases/ubuntu xenial main" > apt-get -y update apt-get install -y --no-install-recommends ${apt_packages[@]} +# 32-bit builds +dpkg --add-architecture i386 +apt-get -y update +apt-get install -y --no-install-recommends libssl-dev:i386 + # Install LLVM/clang-4.0. mkdir clang-tmp git clone -n --depth 1 https://chromium.googlesource.com/chromium/src/tools/clang clang-tmp/clang diff --git a/security/nss/automation/taskcluster/graph/src/extend.js b/security/nss/automation/taskcluster/graph/src/extend.js index 8373c5e6b431..2c5aff89759a 100644 --- a/security/nss/automation/taskcluster/graph/src/extend.js +++ b/security/nss/automation/taskcluster/graph/src/extend.js @@ -63,11 +63,6 @@ queue.filter(task => { if (task.collection == "make") { return false; } - - // Disable mpi tests for now on 32-bit builds (bug 1362392) - if (task.platform == "linux32") { - return false; - } } @@ -168,6 +163,7 @@ export default async function main() { }); await scheduleFuzzing(); + await scheduleFuzzing32(); await scheduleTools(); @@ -415,6 +411,110 @@ async function scheduleFuzzing() { return queue.submit(); } +async function scheduleFuzzing32() { + let base = { + env: { + ASAN_OPTIONS: "allocator_may_return_null=1:detect_stack_use_after_return=1", + UBSAN_OPTIONS: "print_stacktrace=1", + NSS_DISABLE_ARENA_FREE_LIST: "1", + NSS_DISABLE_UNLOAD: "1", + CC: "clang", + CCC: "clang++" + }, + features: ["allowPtrace"], + platform: "linux32", + collection: "fuzz", + image: FUZZ_IMAGE + }; + + // Build base definition. + let build_base = merge({ + command: [ + "/bin/bash", + "-c", + "bin/checkout.sh && " + + "nss/automation/taskcluster/scripts/build_gyp.sh -g -v --fuzz -m32" + ], + artifacts: { + public: { + expires: 24 * 7, + type: "directory", + path: "/home/worker/artifacts" + } + }, + kind: "build", + symbol: "B" + }, base); + + // The task that builds NSPR+NSS. + let task_build = queue.scheduleTask(merge(build_base, { + name: "Linux 32 (debug, fuzz)" + })); + + // The task that builds NSPR+NSS (TLS fuzzing mode). + let task_build_tls = queue.scheduleTask(merge(build_base, { + name: "Linux 32 (debug, TLS fuzz)", + symbol: "B", + group: "TLS", + command: [ + "/bin/bash", + "-c", + "bin/checkout.sh && " + + "nss/automation/taskcluster/scripts/build_gyp.sh -g -v --fuzz=tls -m32" + ], + })); + + // Schedule tests. + queue.scheduleTask(merge(base, { + parent: task_build_tls, + name: "Gtests", + command: [ + "/bin/bash", + "-c", + "bin/checkout.sh && nss/automation/taskcluster/scripts/run_tests.sh" + ], + env: {GTESTFILTER: "*Fuzz*"}, + tests: "ssl_gtests gtests", + cycle: "standard", + symbol: "Gtest", + kind: "test" + })); + + // Schedule fuzzing runs. + let run_base = merge(base, {parent: task_build, kind: "test"}); + scheduleFuzzingRun(run_base, "CertDN", "certDN", 4096); + scheduleFuzzingRun(run_base, "QuickDER", "quickder", 10000); + + // Schedule MPI fuzzing runs. + let mpi_base = merge(run_base, {group: "MPI"}); + let mpi_names = ["add", "addmod", "div", "expmod", "mod", "mulmod", "sqr", + "sqrmod", "sub", "submod"]; + for (let name of mpi_names) { + scheduleFuzzingRun(mpi_base, `MPI (${name})`, `mpi-${name}`, 4096, name); + } + scheduleFuzzingRun(mpi_base, `MPI (invmod)`, `mpi-invmod`, 256, "invmod"); + + // Schedule TLS fuzzing runs (non-fuzzing mode). + let tls_base = merge(run_base, {group: "TLS"}); + scheduleFuzzingRun(tls_base, "TLS Client", "tls-client", 20000, "client-nfm", + "tls-client-no_fuzzer_mode"); + scheduleFuzzingRun(tls_base, "TLS Server", "tls-server", 20000, "server-nfm", + "tls-server-no_fuzzer_mode"); + scheduleFuzzingRun(tls_base, "DTLS Client", "dtls-client", 20000, + "dtls-client-nfm", "dtls-client-no_fuzzer_mode"); + scheduleFuzzingRun(tls_base, "DTLS Server", "dtls-server", 20000, + "dtls-server-nfm", "dtls-server-no_fuzzer_mode"); + + // Schedule TLS fuzzing runs (fuzzing mode). + let tls_fm_base = merge(tls_base, {parent: task_build_tls}); + scheduleFuzzingRun(tls_fm_base, "TLS Client", "tls-client", 20000, "client"); + scheduleFuzzingRun(tls_fm_base, "TLS Server", "tls-server", 20000, "server"); + scheduleFuzzingRun(tls_fm_base, "DTLS Client", "dtls-client", 20000, "dtls-client"); + scheduleFuzzingRun(tls_fm_base, "DTLS Server", "dtls-server", 20000, "dtls-server"); + + return queue.submit(); +} + /*****************************************************************************/ async function scheduleTestBuilds(base, args = "") { diff --git a/security/nss/automation/taskcluster/graph/src/try_syntax.js b/security/nss/automation/taskcluster/graph/src/try_syntax.js index 00de7e98720d..eddb7c21a809 100644 --- a/security/nss/automation/taskcluster/graph/src/try_syntax.js +++ b/security/nss/automation/taskcluster/graph/src/try_syntax.js @@ -23,7 +23,7 @@ function parseOptions(opts) { // Parse platforms. let allPlatforms = ["linux", "linux64", "linux64-asan", "win64", - "linux64-make", "linux-make", "linux64-fuzz", "aarch64"]; + "linux64-make", "linux-make", "linux-fuzz", "linux64-fuzz", "aarch64"]; let platforms = intersect(opts.platform.split(/\s*,\s*/), allPlatforms); // If the given value is nonsense or "none" default to all platforms. @@ -104,6 +104,7 @@ function filter(opts) { let found = opts.platforms.some(platform => { let aliases = { "linux": "linux32", + "linux-fuzz": "linux32", "linux64-asan": "linux64", "linux64-fuzz": "linux64", "linux64-make": "linux64", @@ -119,7 +120,7 @@ function filter(opts) { keep &= coll("asan"); } else if (platform == "linux64-make" || platform == "linux-make") { keep &= coll("make"); - } else if (platform == "linux64-fuzz") { + } else if (platform == "linux64-fuzz" || platform == "linux-fuzz") { keep &= coll("fuzz"); } else { keep &= coll("opt") || coll("debug"); diff --git a/security/nss/cmd/crmftest/testcrmf.c b/security/nss/cmd/crmftest/testcrmf.c index fefa6894db4f..cbc680b08fc9 100644 --- a/security/nss/cmd/crmftest/testcrmf.c +++ b/security/nss/cmd/crmftest/testcrmf.c @@ -1261,11 +1261,13 @@ DoChallengeResponse(SECKEYPrivateKey *privKey, return 908; } keyID = PK11_MakeIDFromPubKey(publicValue); + SECITEM_FreeItem(publicValue, PR_TRUE); if (keyID == NULL) { printf("Could not make the keyID from the public value\n"); return 909; } foundPrivKey = PK11_FindKeyByKeyID(privKey->pkcs11Slot, keyID, &pwdata); + SECITEM_FreeItem(keyID, PR_TRUE); if (foundPrivKey == NULL) { printf("Could not find the private key corresponding to the public" " value.\n"); diff --git a/security/nss/cmd/mpitests/mpitests.gyp b/security/nss/cmd/mpitests/mpitests.gyp index e594e17b3696..346d23131828 100644 --- a/security/nss/cmd/mpitests/mpitests.gyp +++ b/security/nss/cmd/mpitests/mpitests.gyp @@ -31,7 +31,18 @@ 'include_dirs': [ '<(DEPTH)/lib/freebl/mpi', '<(DEPTH)/lib/util', - ] + ], + # This uses test builds and has to set defines for MPI. + 'conditions': [ + [ 'target_arch=="ia32"', { + 'defines': [ + 'MP_USE_UINT_DIGIT', + 'MP_ASSEMBLY_MULTIPLY', + 'MP_ASSEMBLY_SQUARE', + 'MP_ASSEMBLY_DIV_2DX1D', + ], + }], + ], }, 'variables': { 'module': 'nss' diff --git a/security/nss/coreconf/coreconf.dep b/security/nss/coreconf/coreconf.dep index 590d1bfaeee3..5182f75552c8 100644 --- a/security/nss/coreconf/coreconf.dep +++ b/security/nss/coreconf/coreconf.dep @@ -10,4 +10,3 @@ */ #error "Do not include this header file." - diff --git a/security/nss/coreconf/fuzz.sh b/security/nss/coreconf/fuzz.sh index c3cf8abf743d..67cb7f59490c 100644 --- a/security/nss/coreconf/fuzz.sh +++ b/security/nss/coreconf/fuzz.sh @@ -24,7 +24,10 @@ if [ "$fuzz_oss" = 1 ]; then gyp_params+=(-Dno_zdefs=1 -Dfuzz_oss=1) else enable_sanitizer asan - enable_ubsan + # Ubsan doesn't build on 32-bit at the moment. Disable it. + if [ "$build_64" = 1 ]; then + enable_ubsan + fi enable_sancov fi diff --git a/security/nss/fuzz/fuzz.gyp b/security/nss/fuzz/fuzz.gyp index a7339b78ca5c..ed1f53d585a6 100644 --- a/security/nss/fuzz/fuzz.gyp +++ b/security/nss/fuzz/fuzz.gyp @@ -88,6 +88,15 @@ '-lcrypto', ], }], + # For test builds we have to set MPI defines. + [ 'target_arch=="ia32"', { + 'defines': [ + 'MP_USE_UINT_DIGIT', + 'MP_ASSEMBLY_MULTIPLY', + 'MP_ASSEMBLY_SQUARE', + 'MP_ASSEMBLY_DIV_2DX1D', + ], + }], ], }, }, diff --git a/security/nss/gtests/freebl_gtest/freebl_gtest.gyp b/security/nss/gtests/freebl_gtest/freebl_gtest.gyp index 546e69aa9548..9a0449553ca5 100644 --- a/security/nss/gtests/freebl_gtest/freebl_gtest.gyp +++ b/security/nss/gtests/freebl_gtest/freebl_gtest.gyp @@ -29,13 +29,6 @@ '<(DEPTH)/lib/pki/pki.gyp:nsspki', '<(DEPTH)/lib/ssl/ssl.gyp:ssl', ], - 'conditions': [ - [ 'ct_verif==1', { - 'defines': [ - 'CT_VERIF', - ], - }], - ], }, { 'target_name': 'prng_gtest', @@ -57,12 +50,38 @@ '<(DEPTH)/lib/pki/pki.gyp:nsspki', '<(DEPTH)/lib/ssl/ssl.gyp:ssl', ], + 'conditions': [ + [ 'OS=="win"', { + 'libraries': [ + 'advapi32.lib', + ], + }], + ], + 'defines': [ + 'NSS_USE_STATIC_LIBS' + ], }, ], 'target_defaults': { 'include_dirs': [ '<(DEPTH)/lib/freebl/mpi', - ] + ], + # For test builds we have to set MPI defines. + 'conditions': [ + [ 'ct_verif==1', { + 'defines': [ + 'CT_VERIF', + ], + }], + [ 'target_arch=="ia32"', { + 'defines': [ + 'MP_USE_UINT_DIGIT', + 'MP_ASSEMBLY_MULTIPLY', + 'MP_ASSEMBLY_SQUARE', + 'MP_ASSEMBLY_DIV_2DX1D', + ], + }], + ], }, 'variables': { 'module': 'nss' diff --git a/security/nss/gtests/freebl_gtest/mpi_unittest.cc b/security/nss/gtests/freebl_gtest/mpi_unittest.cc index 1f769c01453f..059183fb6df4 100644 --- a/security/nss/gtests/freebl_gtest/mpi_unittest.cc +++ b/security/nss/gtests/freebl_gtest/mpi_unittest.cc @@ -53,13 +53,39 @@ class MPITest : public ::testing::Test { mp_clear(&a); mp_clear(&b); } + + void TestDiv(const std::string a_string, const std::string b_string, + const std::string result) { + mp_int a, b, c; + MP_DIGITS(&a) = 0; + MP_DIGITS(&b) = 0; + MP_DIGITS(&c) = 0; + ASSERT_EQ(MP_OKAY, mp_init(&a)); + ASSERT_EQ(MP_OKAY, mp_init(&b)); + ASSERT_EQ(MP_OKAY, mp_init(&c)); + + mp_read_radix(&a, a_string.c_str(), 16); + mp_read_radix(&b, b_string.c_str(), 16); + mp_read_radix(&c, result.c_str(), 16); + EXPECT_EQ(MP_OKAY, mp_div(&a, &b, &a, &b)); + EXPECT_EQ(0, mp_cmp(&a, &c)); + + mp_clear(&a); + mp_clear(&b); + mp_clear(&c); + } }; TEST_F(MPITest, MpiCmp01Test) { TestCmp("0", "1", -1); } TEST_F(MPITest, MpiCmp10Test) { TestCmp("1", "0", 1); } TEST_F(MPITest, MpiCmp00Test) { TestCmp("0", "0", 0); } TEST_F(MPITest, MpiCmp11Test) { TestCmp("1", "1", 0); } +TEST_F(MPITest, MpiDiv32ErrorTest) { + TestDiv("FFFF00FFFFFFFF000000000000", "FFFF00FFFFFFFFFF", "FFFFFFFFFF"); +} +#ifdef NSS_X64 +// This tests assumes 64-bit mp_digits. TEST_F(MPITest, MpiCmpUnalignedTest) { mp_int a, b, c; MP_DIGITS(&a) = 0; @@ -90,6 +116,7 @@ TEST_F(MPITest, MpiCmpUnalignedTest) { mp_clear(&b); mp_clear(&c); } +#endif // This test is slow. Disable it by default so we can run these tests on CI. class DISABLED_MPITest : public ::testing::Test {}; diff --git a/security/nss/gtests/util_gtest/util_gtest.gyp b/security/nss/gtests/util_gtest/util_gtest.gyp index 25711ddbd7e4..d0ea99e5d246 100644 --- a/security/nss/gtests/util_gtest/util_gtest.gyp +++ b/security/nss/gtests/util_gtest/util_gtest.gyp @@ -13,7 +13,7 @@ 'sources': [ 'util_utf8_unittest.cc', 'util_b64_unittest.cc', - 'util_pkcs11uri_unittest.cc', + 'util_pkcs11uri_unittest.cc', '<(DEPTH)/gtests/common/gtests.cc', ], 'dependencies': [ @@ -29,7 +29,17 @@ '<(DEPTH)/lib/dev/dev.gyp:nssdev', '<(DEPTH)/lib/pki/pki.gyp:nsspki', '<(DEPTH)/lib/ssl/ssl.gyp:ssl', - ] + ], + 'conditions': [ + [ 'OS=="win"', { + 'libraries': [ + 'advapi32.lib', + ], + }], + ], + 'defines': [ + 'NSS_USE_STATIC_LIBS' + ], } ], 'target_defaults': { diff --git a/security/nss/gtests/util_gtest/util_pkcs11uri_unittest.cc b/security/nss/gtests/util_gtest/util_pkcs11uri_unittest.cc index c8d003b4bb7b..e64d8a74443d 100644 --- a/security/nss/gtests/util_gtest/util_pkcs11uri_unittest.cc +++ b/security/nss/gtests/util_gtest/util_pkcs11uri_unittest.cc @@ -31,13 +31,17 @@ class PK11URITest : public ::testing::Test { size_t i; for (i = 0; i < num_pattrs; i++) { const char *value = PK11URI_GetPathAttribute(tmp.get(), pattrs[i].name); - ASSERT_TRUE(value); - ASSERT_EQ(std::string(value), std::string(pattrs[i].value)); + EXPECT_TRUE(value); + if (value) { + EXPECT_EQ(std::string(value), std::string(pattrs[i].value)); + } } for (i = 0; i < num_qattrs; i++) { const char *value = PK11URI_GetQueryAttribute(tmp.get(), qattrs[i].name); - ASSERT_TRUE(value); - ASSERT_EQ(std::string(value), std::string(qattrs[i].value)); + EXPECT_TRUE(value); + if (value) { + EXPECT_EQ(std::string(value), std::string(qattrs[i].value)); + } } } @@ -48,8 +52,10 @@ class PK11URITest : public ::testing::Test { PK11URI_CreateURI(pattrs, num_pattrs, qattrs, num_qattrs)); ASSERT_TRUE(tmp); char *out = PK11URI_FormatURI(nullptr, tmp.get()); - ASSERT_TRUE(out); - ASSERT_EQ(std::string(out), formatted); + EXPECT_TRUE(out); + if (out) { + EXPECT_EQ(std::string(out), formatted); + } PORT_Free(out); } @@ -67,13 +73,17 @@ class PK11URITest : public ::testing::Test { size_t i; for (i = 0; i < num_pattrs; i++) { const char *value = PK11URI_GetPathAttribute(tmp.get(), pattrs[i].name); - ASSERT_TRUE(value); - ASSERT_EQ(std::string(value), std::string(pattrs[i].value)); + EXPECT_TRUE(value); + if (value) { + EXPECT_EQ(std::string(value), std::string(pattrs[i].value)); + } } for (i = 0; i < num_qattrs; i++) { const char *value = PK11URI_GetQueryAttribute(tmp.get(), qattrs[i].name); - ASSERT_TRUE(value); - ASSERT_EQ(std::string(value), std::string(qattrs[i].value)); + EXPECT_TRUE(value); + if (value) { + EXPECT_EQ(std::string(value), std::string(qattrs[i].value)); + } } } @@ -81,9 +91,11 @@ class PK11URITest : public ::testing::Test { ScopedPK11URI tmp(PK11URI_ParseURI(str.c_str())); ASSERT_TRUE(tmp); char *out = PK11URI_FormatURI(nullptr, tmp.get()); - ASSERT_TRUE(out); - ASSERT_EQ(std::string(out), formatted); - PORT_Free(out); + EXPECT_TRUE(out); + if (out) { + EXPECT_EQ(std::string(out), formatted); + PORT_Free(out); + } } protected: diff --git a/security/nss/lib/freebl/mpi/mpi.c b/security/nss/lib/freebl/mpi/mpi.c index e4b26453f2e0..f7784c8d9d3b 100644 --- a/security/nss/lib/freebl/mpi/mpi.c +++ b/security/nss/lib/freebl/mpi/mpi.c @@ -2859,6 +2859,9 @@ void s_mp_exch(mp_int *a, mp_int *b) { mp_int tmp; + if (!a || !b) { + return; + } tmp = *a; *a = *b; @@ -4086,7 +4089,7 @@ s_mpv_sqr_add_prop(const mp_digit *pa, mp_size a_len, mp_digit *ps) } #endif -#if (defined(MP_NO_MP_WORD) || defined(MP_NO_DIV_WORD)) && !defined(MP_ASSEMBLY_DIV_2DX1D) +#if !defined(MP_ASSEMBLY_DIV_2DX1D) /* ** Divide 64-bit (Nhi,Nlo) by 32-bit divisor, which must be normalized ** so its high bit is 1. This code is from NSPR. @@ -4164,11 +4167,7 @@ mp_err s_mp_div(mp_int *rem, /* i: dividend, o: remainder */ mp_int *quot) /* i: 0; o: quotient */ { mp_int part, t; -#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_DIV_WORD) - mp_word q_msd; -#else mp_digit q_msd; -#endif mp_err res; mp_digit d; mp_digit div_msd; @@ -4213,7 +4212,7 @@ mp_err s_mp_div(mp_int *rem, /* i: dividend, o: remainder */ MP_USED(&part) = MP_USED(div); /* We have now truncated the part of the remainder to the same length as - * the divisor. If part is smaller than div, extend part by one digit. */ + * the divisor. If part is smaller than div, extend part by one digit. */ if (s_mp_cmp(&part, div) < 0) { --unusedRem; #if MP_ARGCHK == 2 @@ -4230,18 +4229,12 @@ mp_err s_mp_div(mp_int *rem, /* i: dividend, o: remainder */ div_msd = MP_DIGIT(div, MP_USED(div) - 1); if (!partExtended) { /* In this case, q_msd /= div_msd is always 1. First, since div_msd is - * normalized to have the high bit set, 2*div_msd > MP_DIGIT_MAX. Since - * we didn't extend part, q_msd >= div_msd. Therefore we know that - * div_msd <= q_msd <= MP_DIGIT_MAX < 2*div_msd. Dividing by div_msd we - * get 1 <= q_msd/div_msd < 2. So q_msd /= div_msd must be 1. */ + * normalized to have the high bit set, 2*div_msd > MP_DIGIT_MAX. Since + * we didn't extend part, q_msd >= div_msd. Therefore we know that + * div_msd <= q_msd <= MP_DIGIT_MAX < 2*div_msd. Dividing by div_msd we + * get 1 <= q_msd/div_msd < 2. So q_msd /= div_msd must be 1. */ q_msd = 1; } else { -#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_DIV_WORD) - q_msd = (q_msd << MP_DIGIT_BIT) | MP_DIGIT(&part, MP_USED(&part) - 2); - q_msd /= div_msd; - if (q_msd == RADIX) - --q_msd; -#else if (q_msd == div_msd) { q_msd = MP_DIGIT_MAX; } else { @@ -4249,7 +4242,6 @@ mp_err s_mp_div(mp_int *rem, /* i: dividend, o: remainder */ MP_CHECKOK(s_mpv_div_2dx1d(q_msd, MP_DIGIT(&part, MP_USED(&part) - 2), div_msd, &q_msd, &r)); } -#endif } #if MP_ARGCHK == 2 assert(q_msd > 0); /* This case should never occur any more. */ @@ -4259,15 +4251,15 @@ mp_err s_mp_div(mp_int *rem, /* i: dividend, o: remainder */ /* See what that multiplies out to */ mp_copy(div, &t); - MP_CHECKOK(s_mp_mul_d(&t, (mp_digit)q_msd)); + MP_CHECKOK(s_mp_mul_d(&t, q_msd)); /* - If it's too big, back it off. We should not have to do this - more than once, or, in rare cases, twice. Knuth describes a - method by which this could be reduced to a maximum of once, but - I didn't implement that here. - * When using s_mpv_div_2dx1d, we may have to do this 3 times. - */ + If it's too big, back it off. We should not have to do this + more than once, or, in rare cases, twice. Knuth describes a + method by which this could be reduced to a maximum of once, but + I didn't implement that here. + When using s_mpv_div_2dx1d, we may have to do this 3 times. + */ for (i = 4; s_mp_cmp(&t, &part) > 0 && i > 0; --i) { --q_msd; MP_CHECKOK(s_mp_sub(&t, div)); /* t -= div */ @@ -4282,11 +4274,11 @@ mp_err s_mp_div(mp_int *rem, /* i: dividend, o: remainder */ s_mp_clamp(rem); /* - Include the digit in the quotient. We allocated enough memory - for any quotient we could ever possibly get, so we should not - have to check for failures here - */ - MP_DIGIT(quot, unusedRem) = (mp_digit)q_msd; + Include the digit in the quotient. We allocated enough memory + for any quotient we could ever possibly get, so we should not + have to check for failures here + */ + MP_DIGIT(quot, unusedRem) = q_msd; } /* Denormalize remainder */ diff --git a/security/nss/lib/pk11wrap/pk11cert.c b/security/nss/lib/pk11wrap/pk11cert.c index 804db6da9878..c1caf5e60ba7 100644 --- a/security/nss/lib/pk11wrap/pk11cert.c +++ b/security/nss/lib/pk11wrap/pk11cert.c @@ -765,7 +765,12 @@ find_certs_from_nickname(const char *nickname, void *wincx) *delimit = ':'; } else { slot = PK11_GetInternalKeySlot(); - token = nssToken_AddRef(PK11Slot_GetNSSToken(slot)); + token = PK11Slot_GetNSSToken(slot); + if (token) { + nssToken_AddRef(token); + } else { + PORT_SetError(SEC_ERROR_NO_TOKEN); + } } if (token) { nssList *certList; diff --git a/security/nss/lib/ssl/ssl3con.c b/security/nss/lib/ssl/ssl3con.c index c6757e60e270..5d3bd0761823 100644 --- a/security/nss/lib/ssl/ssl3con.c +++ b/security/nss/lib/ssl/ssl3con.c @@ -38,13 +38,6 @@ #include "zlib.h" #endif -#ifndef PK11_SETATTRS -#define PK11_SETATTRS(x, id, v, l) \ - (x)->type = (id); \ - (x)->pValue = (v); \ - (x)->ulValueLen = (l); -#endif - static PK11SymKey *ssl3_GenerateRSAPMS(sslSocket *ss, ssl3CipherSpec *spec, PK11SlotInfo *serverKeySlot); static SECStatus ssl3_DeriveMasterSecret(sslSocket *ss, PK11SymKey *pms); diff --git a/security/nss/lib/ssl/ssl3ecc.c b/security/nss/lib/ssl/ssl3ecc.c index 983b97d232e7..2b2a61116566 100644 --- a/security/nss/lib/ssl/ssl3ecc.c +++ b/security/nss/lib/ssl/ssl3ecc.c @@ -31,13 +31,6 @@ #include -#ifndef PK11_SETATTRS -#define PK11_SETATTRS(x, id, v, l) \ - (x)->type = (id); \ - (x)->pValue = (v); \ - (x)->ulValueLen = (l); -#endif - SECStatus ssl_NamedGroup2ECParams(PLArenaPool *arena, const sslNamedGroupDef *ecGroup, SECKEYECParams *params) diff --git a/security/nss/lib/ssl/ssl3exthandle.c b/security/nss/lib/ssl/ssl3exthandle.c index bbec1c74fe39..c9c99aba9c12 100644 --- a/security/nss/lib/ssl/ssl3exthandle.c +++ b/security/nss/lib/ssl/ssl3exthandle.c @@ -16,14 +16,16 @@ #include "ssl3exthandle.h" #include "tls13exthandle.h" /* For tls13_ServerSendStatusRequestXtn. */ -static SECStatus ssl3_ParseEncryptedSessionTicket(sslSocket *ss, - SECItem *data, EncryptedSessionTicket *enc_session_ticket); static SECStatus ssl3_AppendToItem(SECItem *item, const unsigned char *buf, PRUint32 bytes); -static SECStatus ssl3_ConsumeFromItem(SECItem *item, unsigned char **buf, PRUint32 bytes); +static SECStatus ssl3_ConsumeFromItem(SECItem *item, unsigned char **buf, + PRUint32 bytes); static SECStatus ssl3_AppendNumberToItem(SECItem *item, PRUint32 num, PRInt32 lenSize); -static SECStatus ssl3_ConsumeFromItem(SECItem *item, unsigned char **buf, PRUint32 bytes); + +PRUint32 ssl_ticket_lifetime = 2 * 24 * 60 * 60; /* 2 days in seconds */ +#define TLS_EX_SESS_TICKET_VERSION (0x0105) +#define TLS_EX_SESS_TICKET_MAC_LENGTH 32 /* * Write bytes. Using this function means the SECItem structure @@ -329,26 +331,28 @@ loser: } static SECStatus -ssl3_ParseEncryptedSessionTicket(sslSocket *ss, SECItem *data, - EncryptedSessionTicket *enc_session_ticket) +ssl3_ParseEncryptedSessionTicket(sslSocket *ss, const SECItem *data, + EncryptedSessionTicket *encryptedTicket) { - if (ssl3_ConsumeFromItem(data, &enc_session_ticket->key_name, + SECItem copy = *data; + + if (ssl3_ConsumeFromItem(©, &encryptedTicket->key_name, SESS_TICKET_KEY_NAME_LEN) != SECSuccess) return SECFailure; - if (ssl3_ConsumeFromItem(data, &enc_session_ticket->iv, + if (ssl3_ConsumeFromItem(©, &encryptedTicket->iv, AES_BLOCK_SIZE) != SECSuccess) return SECFailure; - if (ssl3_ConsumeHandshakeVariable(ss, &enc_session_ticket->encrypted_state, - 2, &data->data, &data->len) != + if (ssl3_ConsumeHandshakeVariable(ss, &encryptedTicket->encrypted_state, + 2, ©.data, ©.len) != SECSuccess) return SECFailure; - if (ssl3_ConsumeFromItem(data, &enc_session_ticket->mac, + if (ssl3_ConsumeFromItem(©, &encryptedTicket->mac, TLS_EX_SESS_TICKET_MAC_LENGTH) != SECSuccess) return SECFailure; - if (data->len != 0) /* Make sure that we have consumed all bytes. */ + if (copy.len != 0) /* Make sure that we have consumed all bytes. */ return SECFailure; return SECSuccess; @@ -877,9 +881,6 @@ ssl3_ClientHandleStatusRequestXtn(const sslSocket *ss, TLSExtensionData *xtnData return SECSuccess; } -PRUint32 ssl_ticket_lifetime = 2 * 24 * 60 * 60; /* 2 days in seconds */ -#define TLS_EX_SESS_TICKET_VERSION (0x0104) - /* * Called from ssl3_SendNewSessionTicket, tls13_SendNewSessionTicket */ @@ -908,20 +909,17 @@ ssl3_EncodeSessionTicket(sslSocket *ss, unsigned char key_name[SESS_TICKET_KEY_NAME_LEN]; PK11SymKey *aes_key = NULL; PK11SymKey *mac_key = NULL; - CK_MECHANISM_TYPE cipherMech = CKM_AES_CBC; PK11Context *aes_ctx; - CK_MECHANISM_TYPE macMech = CKM_SHA256_HMAC; PK11Context *hmac_ctx = NULL; unsigned char computed_mac[TLS_EX_SESS_TICKET_MAC_LENGTH]; unsigned int computed_mac_length; unsigned char iv[AES_BLOCK_SIZE]; SECItem ivItem; SECItem *srvName = NULL; - PRUint32 srvNameLen = 0; CK_MECHANISM_TYPE msWrapMech = 0; /* dummy default value, * must be >= 0 */ ssl3CipherSpec *spec; - SECItem alpnSelection = { siBuffer, NULL, 0 }; + SECItem *alpnSelection = NULL; SSL_TRC(3, ("%d: SSL3[%d]: send session_ticket handshake", SSL_GETPID(), ss->fd)); @@ -930,7 +928,7 @@ ssl3_EncodeSessionTicket(sslSocket *ss, PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); if (ss->opt.requestCertificate && ss->sec.ci.sid->peerCert) { - cert_length = 3 + ss->sec.ci.sid->peerCert->derCert.len; + cert_length = 2 + ss->sec.ci.sid->peerCert->derCert.len; } /* Get IV and encryption keys */ @@ -976,17 +974,14 @@ ssl3_EncodeSessionTicket(sslSocket *ss, } /* Prep to send negotiated name */ srvName = &ss->sec.ci.sid->u.ssl3.srvName; - if (srvName->data && srvName->len) { - srvNameLen = 2 + srvName->len; /* len bytes + name len */ - } - if (ss->xtnData.nextProtoState != SSL_NEXT_PROTO_NO_SUPPORT && - ss->xtnData.nextProto.data) { - alpnSelection = ss->xtnData.nextProto; - } + PORT_Assert(ss->xtnData.nextProtoState == SSL_NEXT_PROTO_SELECTED || + ss->xtnData.nextProtoState == SSL_NEXT_PROTO_NEGOTIATED || + ss->xtnData.nextProto.len == 0); + alpnSelection = &ss->xtnData.nextProto; ciphertext_length = - sizeof(PRUint16) /* ticket_version */ + sizeof(PRUint16) /* ticket version */ + sizeof(SSL3ProtocolVersion) /* ssl_version */ + sizeof(ssl3CipherSuite) /* ciphersuite */ + 1 /* compression */ @@ -998,12 +993,11 @@ ssl3_EncodeSessionTicket(sslSocket *ss, + ms_item.len /* master_secret */ + 1 /* client_auth_type */ + cert_length /* cert */ - + 1 /* server name type */ - + srvNameLen /* name len + length field */ + + 2 + srvName->len /* name len + length field */ + 1 /* extendedMasterSecretUsed */ + sizeof(ticket->ticket_lifetime_hint) /* ticket lifetime hint */ + sizeof(ticket->flags) /* ticket flags */ - + 1 + alpnSelection.len /* npn value + length field. */ + + 1 + alpnSelection->len /* alpn value + length field */ + 4; /* maxEarlyData */ #ifdef UNSAFE_FUZZER_MODE padding_length = 0; @@ -1019,7 +1013,7 @@ ssl3_EncodeSessionTicket(sslSocket *ss, plaintext = plaintext_item; - /* ticket_version */ + /* ticket version */ rv = ssl3_AppendNumberToItem(&plaintext, TLS_EX_SESS_TICKET_VERSION, sizeof(PRUint16)); if (rv != SECSuccess) @@ -1084,13 +1078,13 @@ ssl3_EncodeSessionTicket(sslSocket *ss, if (rv != SECSuccess) goto loser; - /* client_identity */ + /* client identity */ if (ss->opt.requestCertificate && ss->sec.ci.sid->peerCert) { rv = ssl3_AppendNumberToItem(&plaintext, CLIENT_AUTH_CERTIFICATE, 1); if (rv != SECSuccess) goto loser; rv = ssl3_AppendNumberToItem(&plaintext, - ss->sec.ci.sid->peerCert->derCert.len, 3); + ss->sec.ci.sid->peerCert->derCert.len, 2); if (rv != SECSuccess) goto loser; rv = ssl3_AppendToItem(&plaintext, @@ -1111,23 +1105,14 @@ ssl3_EncodeSessionTicket(sslSocket *ss, if (rv != SECSuccess) goto loser; - if (srvNameLen) { - /* Name Type (sni_host_name) */ - rv = ssl3_AppendNumberToItem(&plaintext, srvName->type, 1); - if (rv != SECSuccess) - goto loser; - /* HostName (length and value) */ - rv = ssl3_AppendNumberToItem(&plaintext, srvName->len, 2); - if (rv != SECSuccess) - goto loser; + /* HostName (length and value) */ + rv = ssl3_AppendNumberToItem(&plaintext, srvName->len, 2); + if (rv != SECSuccess) + goto loser; + if (srvName->len) { rv = ssl3_AppendToItem(&plaintext, srvName->data, srvName->len); if (rv != SECSuccess) goto loser; - } else { - /* No Name */ - rv = ssl3_AppendNumberToItem(&plaintext, (char)TLS_STE_NO_SERVER_NAME, 1); - if (rv != SECSuccess) - goto loser; } /* extendedMasterSecretUsed */ @@ -1142,13 +1127,14 @@ ssl3_EncodeSessionTicket(sslSocket *ss, if (rv != SECSuccess) goto loser; - /* NPN value. */ - PORT_Assert(alpnSelection.len < 256); - rv = ssl3_AppendNumberToItem(&plaintext, alpnSelection.len, 1); + /* ALPN value. */ + PORT_Assert(alpnSelection->len < 256); + rv = ssl3_AppendNumberToItem(&plaintext, alpnSelection->len, 1); if (rv != SECSuccess) goto loser; - if (alpnSelection.len) { - rv = ssl3_AppendToItem(&plaintext, alpnSelection.data, alpnSelection.len); + if (alpnSelection->len) { + rv = ssl3_AppendToItem(&plaintext, alpnSelection->data, + alpnSelection->len); if (rv != SECSuccess) goto loser; } @@ -1172,7 +1158,7 @@ ssl3_EncodeSessionTicket(sslSocket *ss, ciphertext.len = plaintext_item.len; PORT_Memcpy(ciphertext.data, plaintext_item.data, plaintext_item.len); #else - aes_ctx = PK11_CreateContextBySymKey(cipherMech, CKA_ENCRYPT, aes_key, &ivItem); + aes_ctx = PK11_CreateContextBySymKey(CKM_AES_CBC, CKA_ENCRYPT, aes_key, &ivItem); if (!aes_ctx) goto loser; @@ -1190,7 +1176,8 @@ ssl3_EncodeSessionTicket(sslSocket *ss, /* Compute MAC. */ PORT_Assert(mac_key); - hmac_ctx = PK11_CreateContextBySymKey(macMech, CKA_SIGN, mac_key, &macParam); + hmac_ctx = PK11_CreateContextBySymKey(CKM_SHA256_HMAC, CKA_SIGN, mac_key, + &macParam); if (!hmac_ctx) goto loser; @@ -1285,455 +1272,525 @@ ssl3_ClientHandleSessionTicketXtn(const sslSocket *ss, TLSExtensionData *xtnData return SECSuccess; } -/* Generic ticket processing code, common to TLS 1.0-1.2 and - * TLS 1.3. */ +static SECStatus +ssl_DecryptSessionTicket(sslSocket *ss, const SECItem *rawTicket, + const EncryptedSessionTicket *encryptedTicket, + SECItem *decryptedTicket) +{ + SECStatus rv; + unsigned char keyName[SESS_TICKET_KEY_NAME_LEN]; + + PK11SymKey *macKey = NULL; + PK11Context *hmacCtx; + unsigned char computedMac[TLS_EX_SESS_TICKET_MAC_LENGTH]; + unsigned int computedMacLength; + SECItem macParam = { siBuffer, NULL, 0 }; + + PK11SymKey *aesKey = NULL; +#ifndef UNSAFE_FUZZER_MODE + PK11Context *aesCtx; + SECItem ivItem; + + unsigned int i; + SSL3Opaque *padding; + SSL3Opaque paddingLength; +#endif + + PORT_Assert(!decryptedTicket->data); + PORT_Assert(!decryptedTicket->len); + if (rawTicket->len < TLS_EX_SESS_TICKET_MAC_LENGTH) { + PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO); + return SECFailure; + } + + /* Get session ticket keys. */ + rv = ssl_GetSessionTicketKeys(ss, keyName, &aesKey, &macKey); + if (rv != SECSuccess) { + SSL_DBG(("%d: SSL[%d]: Unable to get/generate session ticket keys.", + SSL_GETPID(), ss->fd)); + return SECFailure; /* code already set */ + } + + /* If the ticket sent by the client was generated under a key different from + * the one we have, bypass ticket processing. This reports success, meaning + * that the handshake completes, but doesn't resume. + */ + if (PORT_Memcmp(encryptedTicket->key_name, keyName, + SESS_TICKET_KEY_NAME_LEN) != 0) { +#ifndef UNSAFE_FUZZER_MODE + SSL_DBG(("%d: SSL[%d]: Session ticket key_name sent mismatch.", + SSL_GETPID(), ss->fd)); + return SECSuccess; +#endif + } + + /* Verify the MAC on the ticket. */ + PORT_Assert(macKey); + hmacCtx = PK11_CreateContextBySymKey(CKM_SHA256_HMAC, CKA_SIGN, macKey, + &macParam); + if (!hmacCtx) { + SSL_DBG(("%d: SSL[%d]: Unable to create HMAC context: %d.", + SSL_GETPID(), ss->fd, PORT_GetError())); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + SSL_DBG(("%d: SSL[%d]: Successfully created HMAC context.", + SSL_GETPID(), ss->fd)); + do { + rv = PK11_DigestBegin(hmacCtx); + if (rv != SECSuccess) { + break; + } + rv = PK11_DigestOp(hmacCtx, rawTicket->data, + rawTicket->len - TLS_EX_SESS_TICKET_MAC_LENGTH); + if (rv != SECSuccess) { + break; + } + rv = PK11_DigestFinal(hmacCtx, computedMac, &computedMacLength, + sizeof(computedMac)); + } while (0); + PK11_DestroyContext(hmacCtx, PR_TRUE); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + if (NSS_SecureMemcmp(computedMac, encryptedTicket->mac, + computedMacLength) != 0) { +#ifndef UNSAFE_FUZZER_MODE + SSL_DBG(("%d: SSL[%d]: Session ticket MAC mismatch.", + SSL_GETPID(), ss->fd)); + PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO); + return SECFailure; +#endif + } + + /* Decrypt the ticket. */ + + /* Plaintext is shorter than the ciphertext due to padding. */ + if (!SECITEM_AllocItem(NULL, decryptedTicket, + encryptedTicket->encrypted_state.len)) { + return SECFailure; /* code already set */ + } + + PORT_Assert(aesKey); +#ifdef UNSAFE_FUZZER_MODE + decryptedTicket->len = encryptedTicket->encrypted_state.len; + PORT_Memcpy(decryptedTicket->data, + encryptedTicket->encrypted_state.data, + encryptedTicket->encrypted_state.len); +#else + ivItem.data = encryptedTicket->iv; + ivItem.len = AES_BLOCK_SIZE; + aesCtx = PK11_CreateContextBySymKey(CKM_AES_CBC, CKA_DECRYPT, aesKey, + &ivItem); + if (!aesCtx) { + SSL_DBG(("%d: SSL[%d]: Unable to create AES context.", + SSL_GETPID(), ss->fd)); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + do { + rv = PK11_CipherOp(aesCtx, decryptedTicket->data, + (int *)&decryptedTicket->len, decryptedTicket->len, + encryptedTicket->encrypted_state.data, + encryptedTicket->encrypted_state.len); + if (rv != SECSuccess) { + break; + } + rv = PK11_Finalize(aesCtx); + if (rv != SECSuccess) { + break; + } + } while (0); + PK11_DestroyContext(aesCtx, PR_TRUE); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + /* Check padding. */ + paddingLength = decryptedTicket->data[decryptedTicket->len - 1]; + if (paddingLength == 0 || paddingLength > AES_BLOCK_SIZE) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + padding = &decryptedTicket->data[decryptedTicket->len - paddingLength]; + for (i = 0; i < paddingLength; i++, padding++) { + if (paddingLength != *padding) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + } + decryptedTicket->len -= paddingLength; +#endif /* UNSAFE_FUZZER_MODE */ + + return SECSuccess; +} + +static SECStatus +ssl_ParseSessionTicket(sslSocket *ss, const SECItem *decryptedTicket, + SessionTicket *parsedTicket) +{ + PRUint32 temp; + SECStatus rv; + + SSL3Opaque *buffer = decryptedTicket->data; + unsigned int len = decryptedTicket->len; + + PORT_Memset(parsedTicket, 0, sizeof(*parsedTicket)); + parsedTicket->valid = PR_FALSE; + + /* If the decrypted ticket is empty, then report success, but leave the + * ticket marked as invalid. */ + if (decryptedTicket->len == 0) { + return SECSuccess; + } + + /* Read ticket version. */ + rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &len); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + /* Skip the ticket if the version is wrong. This won't result in a + * handshake failure, just a failure to resume. */ + if (temp != TLS_EX_SESS_TICKET_VERSION) { + return SECSuccess; + } + + /* Read SSLVersion. */ + rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &len); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + parsedTicket->ssl_version = (SSL3ProtocolVersion)temp; + if (!ssl3_VersionIsSupported(ss->protocolVariant, + parsedTicket->ssl_version)) { + /* This socket doesn't support the version from the ticket. */ + return SECSuccess; + } + + /* Read cipher_suite. */ + rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &len); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + parsedTicket->cipher_suite = (ssl3CipherSuite)temp; + + /* Read compression_method. */ + rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + parsedTicket->compression_method = (SSLCompressionMethod)temp; + + /* Read cipher spec parameters. */ + rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + parsedTicket->authType = (SSLAuthType)temp; + rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + parsedTicket->authKeyBits = temp; + rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + parsedTicket->keaType = (SSLKEAType)temp; + rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + parsedTicket->keaKeyBits = temp; + + /* Read the optional named curve. */ + rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + if (parsedTicket->authType == ssl_auth_ecdsa || + parsedTicket->authType == ssl_auth_ecdh_rsa || + parsedTicket->authType == ssl_auth_ecdh_ecdsa) { + const sslNamedGroupDef *group = + ssl_LookupNamedGroup((SSLNamedGroup)temp); + if (!group || group->keaType != ssl_kea_ecdh) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + parsedTicket->namedCurve = group; + } + + /* Read the master secret (and how it is wrapped). */ + rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + PORT_Assert(temp == PR_TRUE || temp == PR_FALSE); + parsedTicket->ms_is_wrapped = (PRBool)temp; + + rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + parsedTicket->msWrapMech = (CK_MECHANISM_TYPE)temp; + + rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &len); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + if (temp == 0 || temp > sizeof(parsedTicket->master_secret)) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + parsedTicket->ms_length = (PRUint16)temp; + + /* Read the master secret. */ + rv = ssl3_ExtConsumeHandshake(ss, parsedTicket->master_secret, + parsedTicket->ms_length, &buffer, &len); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + /* Read client identity */ + rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + parsedTicket->client_auth_type = (ClientAuthenticationType)temp; + switch (parsedTicket->client_auth_type) { + case CLIENT_AUTH_ANONYMOUS: + break; + case CLIENT_AUTH_CERTIFICATE: + rv = ssl3_ExtConsumeHandshakeVariable(ss, &parsedTicket->peer_cert, 2, + &buffer, &len); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + break; + default: + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + /* Read timestamp. */ + rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + parsedTicket->timestamp = temp; + + /* Read server name */ + rv = ssl3_ExtConsumeHandshakeVariable(ss, &parsedTicket->srvName, 2, + &buffer, &len); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + /* Read extendedMasterSecretUsed */ + rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + PORT_Assert(temp == PR_TRUE || temp == PR_FALSE); + parsedTicket->extendedMasterSecretUsed = (PRBool)temp; + + rv = ssl3_ExtConsumeHandshake(ss, &temp, 4, &buffer, &len); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + parsedTicket->flags = PR_ntohl(temp); + + rv = ssl3_ExtConsumeHandshakeVariable(ss, &parsedTicket->alpnSelection, 1, + &buffer, &len); + PORT_Assert(parsedTicket->alpnSelection.len < 256); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + parsedTicket->maxEarlyData = temp; + +#ifndef UNSAFE_FUZZER_MODE + /* Done parsing. Check that all bytes have been consumed. */ + if (len != 0) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } +#endif + + parsedTicket->valid = PR_TRUE; + return SECSuccess; +} + +static SECStatus +ssl_CreateSIDFromTicket(sslSocket *ss, const SECItem *rawTicket, + SessionTicket *parsedTicket, sslSessionID **out) +{ + sslSessionID *sid; + SECStatus rv; + + sid = ssl3_NewSessionID(ss, PR_TRUE); + if (sid == NULL) { + return SECFailure; + } + + /* Copy over parameters. */ + sid->version = parsedTicket->ssl_version; + sid->u.ssl3.cipherSuite = parsedTicket->cipher_suite; + sid->u.ssl3.compression = parsedTicket->compression_method; + sid->authType = parsedTicket->authType; + sid->authKeyBits = parsedTicket->authKeyBits; + sid->keaType = parsedTicket->keaType; + sid->keaKeyBits = parsedTicket->keaKeyBits; + sid->namedCurve = parsedTicket->namedCurve; + + rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.locked.sessionTicket.ticket, + rawTicket); + if (rv != SECSuccess) { + goto loser; + } + sid->u.ssl3.locked.sessionTicket.flags = parsedTicket->flags; + sid->u.ssl3.locked.sessionTicket.max_early_data_size = + parsedTicket->maxEarlyData; + + if (parsedTicket->ms_length > + sizeof(sid->u.ssl3.keys.wrapped_master_secret)) { + goto loser; + } + PORT_Memcpy(sid->u.ssl3.keys.wrapped_master_secret, + parsedTicket->master_secret, parsedTicket->ms_length); + sid->u.ssl3.keys.wrapped_master_secret_len = parsedTicket->ms_length; + sid->u.ssl3.masterWrapMech = parsedTicket->msWrapMech; + sid->u.ssl3.keys.msIsWrapped = parsedTicket->ms_is_wrapped; + sid->u.ssl3.masterValid = PR_TRUE; + sid->u.ssl3.keys.resumable = PR_TRUE; + sid->u.ssl3.keys.extendedMasterSecretUsed = parsedTicket->extendedMasterSecretUsed; + + /* Copy over client cert from session ticket if there is one. */ + if (parsedTicket->peer_cert.data != NULL) { + PORT_Assert(!sid->peerCert); + sid->peerCert = CERT_NewTempCertificate(ss->dbHandle, + &parsedTicket->peer_cert, + NULL, PR_FALSE, PR_TRUE); + if (!sid->peerCert) { + goto loser; + } + } + + /* Transfer ownership of the remaining items. */ + if (parsedTicket->srvName.data != NULL) { + SECITEM_FreeItem(&sid->u.ssl3.srvName, PR_FALSE); + rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.srvName, + &parsedTicket->srvName); + if (rv != SECSuccess) { + goto loser; + } + } + if (parsedTicket->alpnSelection.data != NULL) { + rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.alpnSelection, + &parsedTicket->alpnSelection); + if (rv != SECSuccess) { + goto loser; + } + } + + *out = sid; + return SECSuccess; + +loser: + ssl_FreeSID(sid); + return SECFailure; +} + +/* Generic ticket processing code, common to all TLS versions. */ SECStatus ssl3_ProcessSessionTicketCommon(sslSocket *ss, SECItem *data) { + EncryptedSessionTicket encryptedTicket; + SECItem decryptedTicket = { siBuffer, NULL, 0 }; + SessionTicket parsedTicket; SECStatus rv; - SECItem *decrypted_state = NULL; - SessionTicket *parsed_session_ticket = NULL; - sslSessionID *sid = NULL; - SSL3Statistics *ssl3stats; - PRUint32 i; - SECItem extension_data; - EncryptedSessionTicket enc_session_ticket; - unsigned char computed_mac[TLS_EX_SESS_TICKET_MAC_LENGTH]; - unsigned int computed_mac_length; - unsigned char key_name[SESS_TICKET_KEY_NAME_LEN]; - PK11SymKey *aes_key = NULL; - PK11SymKey *mac_key = NULL; - PK11Context *hmac_ctx; - CK_MECHANISM_TYPE macMech = CKM_SHA256_HMAC; - PK11Context *aes_ctx; - CK_MECHANISM_TYPE cipherMech = CKM_AES_CBC; - unsigned char *padding; - PRUint32 padding_length; - unsigned char *buffer; - unsigned int buffer_len; - PRUint32 temp; - SECItem cert_item; - PRUint32 nameType; - SECItem macParam = { siBuffer, NULL, 0 }; - SECItem alpn_item; - SECItem ivItem; - /* Turn off stateless session resumption if the client sends a - * SessionTicket extension, even if the extension turns out to be - * malformed (ss->sec.ci.sid is non-NULL when doing session - * renegotiation.) - */ if (ss->sec.ci.sid != NULL) { ss->sec.uncache(ss->sec.ci.sid); ssl_FreeSID(ss->sec.ci.sid); ss->sec.ci.sid = NULL; } - extension_data.data = data->data; /* Keep a copy for future use. */ - extension_data.len = data->len; - - if (ssl3_ParseEncryptedSessionTicket(ss, data, &enc_session_ticket) != - SECSuccess) { - return SECSuccess; /* Pretend it isn't there */ - } - - /* Get session ticket keys. */ - rv = ssl_GetSessionTicketKeys(ss, key_name, &aes_key, &mac_key); + rv = ssl3_ParseEncryptedSessionTicket(ss, data, &encryptedTicket); if (rv != SECSuccess) { - SSL_DBG(("%d: SSL[%d]: Unable to get/generate session ticket keys.", - SSL_GETPID(), ss->fd)); - goto loser; + PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO); + return SECFailure; } - /* If the ticket sent by the client was generated under a key different - * from the one we have, bypass ticket processing. - */ - if (PORT_Memcmp(enc_session_ticket.key_name, key_name, - SESS_TICKET_KEY_NAME_LEN) != 0) { -#ifndef UNSAFE_FUZZER_MODE - SSL_DBG(("%d: SSL[%d]: Session ticket key_name sent mismatch.", - SSL_GETPID(), ss->fd)); - goto no_ticket; -#endif - } - - /* Verify the MAC on the ticket. MAC verification may also - * fail if the MAC key has been recently refreshed. - */ - PORT_Assert(mac_key); - hmac_ctx = PK11_CreateContextBySymKey(macMech, CKA_SIGN, mac_key, &macParam); - if (!hmac_ctx) { - SSL_DBG(("%d: SSL[%d]: Unable to create HMAC context: %d.", - SSL_GETPID(), ss->fd, PORT_GetError())); - goto no_ticket; - } else { - SSL_DBG(("%d: SSL[%d]: Successfully created HMAC context.", - SSL_GETPID(), ss->fd)); - } - rv = PK11_DigestBegin(hmac_ctx); + rv = ssl_DecryptSessionTicket(ss, data, &encryptedTicket, + &decryptedTicket); if (rv != SECSuccess) { - PK11_DestroyContext(hmac_ctx, PR_TRUE); - goto no_ticket; + PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO); + return SECFailure; } - rv = PK11_DigestOp(hmac_ctx, extension_data.data, - extension_data.len - - TLS_EX_SESS_TICKET_MAC_LENGTH); + + rv = ssl_ParseSessionTicket(ss, &decryptedTicket, &parsedTicket); if (rv != SECSuccess) { - PK11_DestroyContext(hmac_ctx, PR_TRUE); - goto no_ticket; - } - rv = PK11_DigestFinal(hmac_ctx, computed_mac, - &computed_mac_length, sizeof(computed_mac)); - PK11_DestroyContext(hmac_ctx, PR_TRUE); - if (rv != SECSuccess) - goto no_ticket; + SSL3Statistics *ssl3stats; - if (NSS_SecureMemcmp(computed_mac, enc_session_ticket.mac, - computed_mac_length) != - 0) { -#ifndef UNSAFE_FUZZER_MODE - SSL_DBG(("%d: SSL[%d]: Session ticket MAC mismatch.", + SSL_DBG(("%d: SSL[%d]: Session ticket parsing failed.", SSL_GETPID(), ss->fd)); - goto no_ticket; -#endif + ssl3stats = SSL_GetStatistics(); + SSL_AtomicIncrementLong(&ssl3stats->hch_sid_ticket_parse_failures); + goto loser; /* code already set */ } - /* We ignore key_name for now. - * This is ok as MAC verification succeeded. - */ + /* Use the ticket if it is valid and unexpired. */ + if (parsedTicket.valid && + parsedTicket.timestamp + ssl_ticket_lifetime > ssl_Time()) { + sslSessionID *sid; - /* Decrypt the ticket. */ - - /* Plaintext is shorter than the ciphertext due to padding. */ - decrypted_state = SECITEM_AllocItem(NULL, NULL, - enc_session_ticket.encrypted_state.len); - - PORT_Assert(aes_key); -#ifdef UNSAFE_FUZZER_MODE - decrypted_state->len = enc_session_ticket.encrypted_state.len; - PORT_Memcpy(decrypted_state->data, - enc_session_ticket.encrypted_state.data, - enc_session_ticket.encrypted_state.len); -#else - ivItem.data = enc_session_ticket.iv; - ivItem.len = AES_BLOCK_SIZE; - aes_ctx = PK11_CreateContextBySymKey(cipherMech, CKA_DECRYPT, - aes_key, &ivItem); - if (!aes_ctx) { - SSL_DBG(("%d: SSL[%d]: Unable to create AES context.", - SSL_GETPID(), ss->fd)); - goto no_ticket; - } - - rv = PK11_CipherOp(aes_ctx, decrypted_state->data, - (int *)&decrypted_state->len, decrypted_state->len, - enc_session_ticket.encrypted_state.data, - enc_session_ticket.encrypted_state.len); - PK11_Finalize(aes_ctx); - PK11_DestroyContext(aes_ctx, PR_TRUE); - if (rv != SECSuccess) - goto no_ticket; - - /* Check padding. */ - padding_length = - (PRUint32)decrypted_state->data[decrypted_state->len - 1]; - if (padding_length == 0 || padding_length > AES_BLOCK_SIZE) - goto no_ticket; - - padding = &decrypted_state->data[decrypted_state->len - padding_length]; - for (i = 0; i < padding_length; i++, padding++) { - if (padding_length != (PRUint32)*padding) - goto no_ticket; - } -#endif - - /* Deserialize session state. */ - buffer = decrypted_state->data; - buffer_len = decrypted_state->len; - - parsed_session_ticket = PORT_ZAlloc(sizeof(SessionTicket)); - if (parsed_session_ticket == NULL) { - rv = SECFailure; - goto loser; - } - - /* Read ticket_version and reject if the version is wrong */ - rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &buffer_len); - if (rv != SECSuccess || temp != TLS_EX_SESS_TICKET_VERSION) - goto no_ticket; - - parsed_session_ticket->ticket_version = (SSL3ProtocolVersion)temp; - - /* Read SSLVersion. */ - rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &buffer_len); - if (rv != SECSuccess) - goto no_ticket; - parsed_session_ticket->ssl_version = (SSL3ProtocolVersion)temp; - - /* Read cipher_suite. */ - rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &buffer_len); - if (rv != SECSuccess) - goto no_ticket; - parsed_session_ticket->cipher_suite = (ssl3CipherSuite)temp; - - /* Read compression_method. */ - rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len); - if (rv != SECSuccess) - goto no_ticket; - parsed_session_ticket->compression_method = (SSLCompressionMethod)temp; - - /* Read cipher spec parameters. */ - rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len); - if (rv != SECSuccess) - goto no_ticket; - parsed_session_ticket->authType = (SSLAuthType)temp; - rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &buffer_len); - if (rv != SECSuccess) - goto no_ticket; - parsed_session_ticket->authKeyBits = temp; - rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len); - if (rv != SECSuccess) - goto no_ticket; - parsed_session_ticket->keaType = (SSLKEAType)temp; - rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &buffer_len); - if (rv != SECSuccess) - goto no_ticket; - parsed_session_ticket->keaKeyBits = temp; - - /* Read the optional named curve. */ - rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len); - if (rv != SECSuccess) - goto no_ticket; - if (parsed_session_ticket->authType == ssl_auth_ecdsa || - parsed_session_ticket->authType == ssl_auth_ecdh_rsa || - parsed_session_ticket->authType == ssl_auth_ecdh_ecdsa) { - const sslNamedGroupDef *group = - ssl_LookupNamedGroup((SSLNamedGroup)temp); - if (!group || group->keaType != ssl_kea_ecdh) { - goto no_ticket; - } - parsed_session_ticket->namedCurve = group; - } - - /* Read wrapped master_secret. */ - rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len); - if (rv != SECSuccess) - goto no_ticket; - parsed_session_ticket->ms_is_wrapped = (PRBool)temp; - - rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &buffer_len); - if (rv != SECSuccess) - goto no_ticket; - parsed_session_ticket->msWrapMech = (CK_MECHANISM_TYPE)temp; - - rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &buffer_len); - if (rv != SECSuccess) - goto no_ticket; - parsed_session_ticket->ms_length = (PRUint16)temp; - if (parsed_session_ticket->ms_length == 0 || /* sanity check MS. */ - parsed_session_ticket->ms_length > - sizeof(parsed_session_ticket->master_secret)) - goto no_ticket; - - /* Allow for the wrapped master secret to be longer. */ - if (buffer_len < parsed_session_ticket->ms_length) - goto no_ticket; - PORT_Memcpy(parsed_session_ticket->master_secret, buffer, - parsed_session_ticket->ms_length); - buffer += parsed_session_ticket->ms_length; - buffer_len -= parsed_session_ticket->ms_length; - - /* Read client_identity */ - rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len); - if (rv != SECSuccess) - goto no_ticket; - parsed_session_ticket->client_identity.client_auth_type = - (ClientAuthenticationType)temp; - switch (parsed_session_ticket->client_identity.client_auth_type) { - case CLIENT_AUTH_ANONYMOUS: - break; - case CLIENT_AUTH_CERTIFICATE: - rv = ssl3_ExtConsumeHandshakeVariable(ss, &cert_item, 3, - &buffer, &buffer_len); - if (rv != SECSuccess) - goto no_ticket; - rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->peer_cert, - &cert_item); - if (rv != SECSuccess) - goto no_ticket; - break; - default: - goto no_ticket; - } - /* Read timestamp. */ - rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &buffer_len); - if (rv != SECSuccess) - goto no_ticket; - parsed_session_ticket->timestamp = temp; - - /* Read server name */ - rv = ssl3_ExtConsumeHandshakeNumber(ss, &nameType, 1, &buffer, &buffer_len); - if (rv != SECSuccess) - goto no_ticket; - if ((PRInt8)nameType != TLS_STE_NO_SERVER_NAME) { - SECItem name_item; - rv = ssl3_ExtConsumeHandshakeVariable(ss, &name_item, 2, &buffer, - &buffer_len); - if (rv != SECSuccess) - goto no_ticket; - rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->srvName, - &name_item); - if (rv != SECSuccess) - goto no_ticket; - parsed_session_ticket->srvName.type = (PRUint8)nameType; - } - - /* Read extendedMasterSecretUsed */ - rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len); - if (rv != SECSuccess) - goto no_ticket; - PORT_Assert(temp == PR_TRUE || temp == PR_FALSE); - parsed_session_ticket->extendedMasterSecretUsed = (PRBool)temp; - - rv = ssl3_ExtConsumeHandshake(ss, &parsed_session_ticket->flags, 4, - &buffer, &buffer_len); - if (rv != SECSuccess) - goto no_ticket; - parsed_session_ticket->flags = PR_ntohl(parsed_session_ticket->flags); - - rv = ssl3_ExtConsumeHandshakeVariable(ss, &alpn_item, 1, &buffer, &buffer_len); - if (rv != SECSuccess) - goto no_ticket; - if (alpn_item.len != 0) { - rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->alpnSelection, - &alpn_item); - if (rv != SECSuccess) - goto no_ticket; - if (alpn_item.len >= 256) - goto no_ticket; - } - - rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &buffer_len); - if (rv != SECSuccess) { - goto no_ticket; - } - parsed_session_ticket->maxEarlyData = temp; - -#ifndef UNSAFE_FUZZER_MODE - /* Done parsing. Check that all bytes have been consumed. */ - if (buffer_len != padding_length) { - goto no_ticket; - } -#endif - - /* Use the ticket if it has not expired, otherwise free the allocated - * memory since the ticket is of no use. - */ - if (parsed_session_ticket->timestamp != 0 && - parsed_session_ticket->timestamp + ssl_ticket_lifetime > - ssl_Time()) { - - sid = ssl3_NewSessionID(ss, PR_TRUE); - if (sid == NULL) { - rv = SECFailure; - goto loser; - } - - /* Copy over parameters. */ - sid->version = parsed_session_ticket->ssl_version; - sid->u.ssl3.cipherSuite = parsed_session_ticket->cipher_suite; - sid->u.ssl3.compression = parsed_session_ticket->compression_method; - sid->authType = parsed_session_ticket->authType; - sid->authKeyBits = parsed_session_ticket->authKeyBits; - sid->keaType = parsed_session_ticket->keaType; - sid->keaKeyBits = parsed_session_ticket->keaKeyBits; - sid->namedCurve = parsed_session_ticket->namedCurve; - - if (SECITEM_CopyItem(NULL, &sid->u.ssl3.locked.sessionTicket.ticket, - &extension_data) != SECSuccess) - goto no_ticket; - sid->u.ssl3.locked.sessionTicket.flags = parsed_session_ticket->flags; - sid->u.ssl3.locked.sessionTicket.max_early_data_size = - parsed_session_ticket->maxEarlyData; - - if (parsed_session_ticket->ms_length > - sizeof(sid->u.ssl3.keys.wrapped_master_secret)) - goto no_ticket; - PORT_Memcpy(sid->u.ssl3.keys.wrapped_master_secret, - parsed_session_ticket->master_secret, - parsed_session_ticket->ms_length); - sid->u.ssl3.keys.wrapped_master_secret_len = - parsed_session_ticket->ms_length; - sid->u.ssl3.masterWrapMech = parsed_session_ticket->msWrapMech; - sid->u.ssl3.keys.msIsWrapped = - parsed_session_ticket->ms_is_wrapped; - sid->u.ssl3.masterValid = PR_TRUE; - sid->u.ssl3.keys.resumable = PR_TRUE; - sid->u.ssl3.keys.extendedMasterSecretUsed = parsed_session_ticket->extendedMasterSecretUsed; - - /* Copy over client cert from session ticket if there is one. */ - if (parsed_session_ticket->peer_cert.data != NULL) { - if (sid->peerCert != NULL) - CERT_DestroyCertificate(sid->peerCert); - sid->peerCert = CERT_NewTempCertificate(ss->dbHandle, - &parsed_session_ticket->peer_cert, NULL, PR_FALSE, PR_TRUE); - if (sid->peerCert == NULL) { - rv = SECFailure; - goto loser; - } - } - if (parsed_session_ticket->srvName.data != NULL) { - if (sid->u.ssl3.srvName.data) { - SECITEM_FreeItem(&sid->u.ssl3.srvName, PR_FALSE); - } - sid->u.ssl3.srvName = parsed_session_ticket->srvName; - parsed_session_ticket->srvName.data = NULL; - } - if (parsed_session_ticket->alpnSelection.data != NULL) { - sid->u.ssl3.alpnSelection = parsed_session_ticket->alpnSelection; - /* So we don't free below. */ - parsed_session_ticket->alpnSelection.data = NULL; + rv = ssl_CreateSIDFromTicket(ss, data, &parsedTicket, &sid); + if (rv != SECSuccess) { + goto loser; /* code already set */ } ss->statelessResume = PR_TRUE; ss->sec.ci.sid = sid; } - if (0) { - no_ticket: - SSL_DBG(("%d: SSL[%d]: Session ticket parsing failed.", - SSL_GETPID(), ss->fd)); - ssl3stats = SSL_GetStatistics(); - SSL_AtomicIncrementLong(&ssl3stats->hch_sid_ticket_parse_failures); - } - rv = SECSuccess; + SECITEM_ZfreeItem(&decryptedTicket, PR_FALSE); + PORT_Memset(&parsedTicket, 0, sizeof(parsedTicket)); + return SECSuccess; loser: - /* ss->sec.ci.sid == sid if it did NOT come here via goto statement - * in that case do not free sid - */ - if (sid && (ss->sec.ci.sid != sid)) { - ssl_FreeSID(sid); - sid = NULL; - } - if (decrypted_state != NULL) { - SECITEM_FreeItem(decrypted_state, PR_TRUE); - decrypted_state = NULL; - } - - if (parsed_session_ticket != NULL) { - if (parsed_session_ticket->peer_cert.data) { - SECITEM_FreeItem(&parsed_session_ticket->peer_cert, PR_FALSE); - } - if (parsed_session_ticket->alpnSelection.data) { - SECITEM_FreeItem(&parsed_session_ticket->alpnSelection, PR_FALSE); - } - if (parsed_session_ticket->srvName.data) { - SECITEM_FreeItem(&parsed_session_ticket->srvName, PR_FALSE); - } - PORT_ZFree(parsed_session_ticket, sizeof(SessionTicket)); - } - - return rv; + SECITEM_ZfreeItem(&decryptedTicket, PR_FALSE); + PORT_Memset(&parsedTicket, 0, sizeof(parsedTicket)); + return SECFailure; } SECStatus diff --git a/security/nss/lib/ssl/ssl3prot.h b/security/nss/lib/ssl/ssl3prot.h index 2088c54bb69c..60a978bfdc5b 100644 --- a/security/nss/lib/ssl/ssl3prot.h +++ b/security/nss/lib/ssl/ssl3prot.h @@ -305,13 +305,6 @@ typedef enum { CLIENT_AUTH_CERTIFICATE = 1 } ClientAuthenticationType; -typedef struct { - ClientAuthenticationType client_auth_type; - union { - SSL3Opaque *certificate_list; - } identity; -} ClientIdentity; - #define SESS_TICKET_KEY_NAME_LEN 16 #define SESS_TICKET_KEY_NAME_PREFIX "NSS!" #define SESS_TICKET_KEY_NAME_PREFIX_LEN 4 @@ -324,8 +317,4 @@ typedef struct { unsigned char *mac; } EncryptedSessionTicket; -#define TLS_EX_SESS_TICKET_MAC_LENGTH 32 - -#define TLS_STE_NO_SERVER_NAME -1 - #endif /* __ssl3proto_h_ */ diff --git a/security/nss/lib/ssl/sslimpl.h b/security/nss/lib/ssl/sslimpl.h index 3d04f1300f66..55fb8cbf2f72 100644 --- a/security/nss/lib/ssl/sslimpl.h +++ b/security/nss/lib/ssl/sslimpl.h @@ -992,7 +992,7 @@ typedef struct SSLWrappedSymWrappingKeyStr { } SSLWrappedSymWrappingKey; typedef struct SessionTicketStr { - PRUint16 ticket_version; + PRBool valid; SSL3ProtocolVersion ssl_version; ssl3CipherSuite cipher_suite; SSLCompressionMethod compression_method; @@ -1010,7 +1010,7 @@ typedef struct SessionTicketStr { PRUint16 ms_length; SSL3Opaque master_secret[48]; PRBool extendedMasterSecretUsed; - ClientIdentity client_identity; + ClientAuthenticationType client_auth_type; SECItem peer_cert; PRUint32 timestamp; PRUint32 flags; diff --git a/security/nss/lib/ssl/sslsnce.c b/security/nss/lib/ssl/sslsnce.c index 92d7f10a9514..cf86edcfab7a 100644 --- a/security/nss/lib/ssl/sslsnce.c +++ b/security/nss/lib/ssl/sslsnce.c @@ -1808,6 +1808,7 @@ ssl_GetSessionTicketKeys(sslSocket *ss, unsigned char *keyName, } if (!ssl_session_ticket_keys.encKey || !ssl_session_ticket_keys.macKey) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure; } diff --git a/security/nss/lib/util/pkcs11uri.c b/security/nss/lib/util/pkcs11uri.c index 1be0585421a1..453440293863 100644 --- a/security/nss/lib/util/pkcs11uri.c +++ b/security/nss/lib/util/pkcs11uri.c @@ -681,7 +681,7 @@ PK11URI_ParseURI(const char *string) result = pk11uri_AllocURI(); if (result == NULL) { - goto fail; + return NULL; } /* Parse the path component and its attributes. */ diff --git a/security/nss/nss-tool/db/dbtool.cc b/security/nss/nss-tool/db/dbtool.cc index 5bdeec56ee51..8c369cf05646 100644 --- a/security/nss/nss-tool/db/dbtool.cc +++ b/security/nss/nss-tool/db/dbtool.cc @@ -7,7 +7,6 @@ #include "scoped_ptrs.h" #include "util.h" -#include #include #include #include @@ -171,24 +170,24 @@ bool DBTool::PathHasDBFiles(std::string path) { std::regex certDBPattern("cert.*\\.db"); std::regex keyDBPattern("key.*\\.db"); - DIR *dir; - if (!(dir = opendir(path.c_str()))) { + PRDir *dir = PR_OpenDir(path.c_str()); + if (!dir) { std::cerr << "Directory " << path << " could not be accessed!" << std::endl; return false; } - struct dirent *ent; + PRDirEntry *ent; bool dbFileExists = false; - while ((ent = readdir(dir))) { - if (std::regex_match(ent->d_name, certDBPattern) || - std::regex_match(ent->d_name, keyDBPattern) || - "secmod.db" == std::string(ent->d_name)) { + while ((ent = PR_ReadDir(dir, PR_SKIP_BOTH))) { + if (std::regex_match(ent->name, certDBPattern) || + std::regex_match(ent->name, keyDBPattern) || + "secmod.db" == std::string(ent->name)) { dbFileExists = true; break; } } - closedir(dir); + (void)PR_CloseDir(dir); return dbFileExists; } diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json index ad82bfbb71dc..775389db89e6 100644 --- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -103909,6 +103909,12 @@ {} ] ], + "html/webappapis/idle-callbacks/callback-xhr-sync.html": [ + [ + "/html/webappapis/idle-callbacks/callback-xhr-sync.html", + {} + ] + ], "html/webappapis/idle-callbacks/cancel-invoked.html": [ [ "/html/webappapis/idle-callbacks/cancel-invoked.html", @@ -191752,6 +191758,10 @@ "ba76964575cdf9b433f26c8a5d7a8183ab5c16e9", "testharness" ], + "html/webappapis/idle-callbacks/callback-xhr-sync.html": [ + "79b4a278f0e35646cfdffeebf8f0523e2772bc9b", + "testharness" + ], "html/webappapis/idle-callbacks/cancel-invoked.html": [ "30787d765fa435c1392bd852559042bf3c2e2553", "testharness" diff --git a/testing/web-platform/tests/encrypted-media/scripts/requestmediakeysystemaccess.js b/testing/web-platform/tests/encrypted-media/scripts/requestmediakeysystemaccess.js index 3fd5412ef67a..a739d38f5433 100644 --- a/testing/web-platform/tests/encrypted-media/scripts/requestmediakeysystemaccess.js +++ b/testing/web-platform/tests/encrypted-media/scripts/requestmediakeysystemaccess.js @@ -84,7 +84,7 @@ function runTest(config, qualifier) { // Tests for trivial configurations. expect_error(config.keysystem, [], 'TypeError', 'Empty supportedConfigurations'); - expect_error(config.keysystem, [{}], 'NotSupportedError', 'Empty configuration'); + expect_config(config.keysystem, [{}], {}, 'Empty configuration'); // Various combinations of supportedConfigurations. expect_config(config.keysystem, [{ @@ -156,13 +156,13 @@ function runTest(config, qualifier) { }], 'NotSupportedError', 'Mismatched audio container/codec (%audiocontenttype)'); expect_config(config.keysystem, [ - {initDataTypes: ['fakeidt'], videoCapabilities: [{contentType: config.videoType}] }, - {initDataTypes: [config.initDataType], videoCapabilities: [{contentType: config.videoType}]} - ], {initDataTypes: [config.initDataType], videoCapabilities: [{contentType: config.videoType}]}, 'Two configurations, one supported'); + {initDataTypes: ['fakeidt']}, + {initDataTypes: [config.initDataType]} + ], {initDataTypes: [config.initDataType]}, 'Two configurations, one supported'); expect_config(config.keysystem, [ - {initDataTypes: [config.initDataType], videoCapabilities: [{contentType: config.videoType}]}, - {videoCapabilities: [{contentType: config.videoType}]} + {initDataTypes: [config.initDataType]}, + {} ], {initDataTypes: [config.initDataType]}, 'Two configurations, both supported'); // Audio MIME type does not support video codecs. diff --git a/testing/web-platform/tests/html/webappapis/idle-callbacks/callback-xhr-sync.html b/testing/web-platform/tests/html/webappapis/idle-callbacks/callback-xhr-sync.html new file mode 100644 index 000000000000..0c759736e502 --- /dev/null +++ b/testing/web-platform/tests/html/webappapis/idle-callbacks/callback-xhr-sync.html @@ -0,0 +1,16 @@ + + + + + + diff --git a/toolkit/components/extensions/Extension.jsm b/toolkit/components/extensions/Extension.jsm index 8ddf3ba11681..ad3b3ea4ac99 100644 --- a/toolkit/components/extensions/Extension.jsm +++ b/toolkit/components/extensions/Extension.jsm @@ -711,6 +711,11 @@ this.Extension = class extends ExtensionData { if (this.remote && processCount !== 1) { throw new Error("Out-of-process WebExtensions are not supported with multiple child processes"); } + if (this.remote && !Services.prefs.getBoolPref("layers.popups.compositing.enabled", false)) { + Cu.reportError(new Error("Remote extensions should not be enabled without also setting " + + "the layers.popups.compositing.enabled preference to true")); + } + // This is filled in the first time an extension child is created. this.parentMessageManager = null; @@ -1095,6 +1100,11 @@ this.Extension = class extends ExtensionData { Services.ppmm.broadcastAsyncMessage("Extension:Shutdown", {id: this.id}); + if (this.rootURI instanceof Ci.nsIJARURI) { + let file = this.rootURI.JARFile.QueryInterface(Ci.nsIFileURL).file; + Services.ppmm.broadcastAsyncMessage("Extension:FlushJarCache", {path: file.path}); + } + MessageChannel.abortResponses({extensionId: this.id}); ExtensionManagement.shutdownExtension(this.uuid); diff --git a/toolkit/components/extensions/moz.build b/toolkit/components/extensions/moz.build index 9bb7f069b5d4..1652b8f04bd6 100644 --- a/toolkit/components/extensions/moz.build +++ b/toolkit/components/extensions/moz.build @@ -58,6 +58,7 @@ MOCHITEST_MANIFESTS += [ MOCHITEST_CHROME_MANIFESTS += ['test/mochitest/chrome.ini'] XPCSHELL_TESTS_MANIFESTS += [ 'test/xpcshell/native_messaging.ini', + 'test/xpcshell/xpcshell-e10s.ini', 'test/xpcshell/xpcshell-remote.ini', 'test/xpcshell/xpcshell.ini', ] diff --git a/toolkit/components/extensions/test/mochitest/head.js b/toolkit/components/extensions/test/mochitest/head.js index f8ae2162f2e7..d92fe1eec460 100644 --- a/toolkit/components/extensions/test/mochitest/head.js +++ b/toolkit/components/extensions/test/mochitest/head.js @@ -11,6 +11,7 @@ var {AppConstants} = SpecialPowers.Cu.import("resource://gre/modules/AppConstant if (location.pathname.includes("test-oop-extensions")) { SpecialPowers.pushPrefEnv({set: [ ["extensions.webextensions.remote", true], + ["layers.popups.compositing.enabled", true], ]}); // We don't want to reset this at the end of the test, so that we don't have // to spawn a new extension child process for each test unit. diff --git a/toolkit/components/extensions/test/xpcshell/head.js b/toolkit/components/extensions/test/xpcshell/head.js index a8f3458bb95f..94945de910b5 100644 --- a/toolkit/components/extensions/test/xpcshell/head.js +++ b/toolkit/components/extensions/test/xpcshell/head.js @@ -5,6 +5,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; /* exported createHttpServer, promiseConsoleOutput, cleanupDir */ Components.utils.import("resource://gre/modules/AppConstants.jsm"); +Components.utils.import("resource://gre/modules/Services.jsm"); Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); Components.utils.import("resource://gre/modules/Timer.jsm"); Components.utils.import("resource://testing-common/AddonTestUtils.jsm"); @@ -27,8 +28,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Schemas", "resource://gre/modules/Schemas.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Services", - "resource://gre/modules/Services.jsm"); + +Services.prefs.setBoolPref("extensions.webextensions.remote", false); ExtensionTestUtils.init(this); diff --git a/toolkit/components/extensions/test/xpcshell/head_e10s.js b/toolkit/components/extensions/test/xpcshell/head_e10s.js new file mode 100644 index 000000000000..55ff2164a10e --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/head_e10s.js @@ -0,0 +1,5 @@ +"use strict"; + +/* globals ExtensionTestUtils */ + +ExtensionTestUtils.remoteContentScripts = true; diff --git a/toolkit/components/extensions/test/xpcshell/head_remote.js b/toolkit/components/extensions/test/xpcshell/head_remote.js index 55ff2164a10e..35eb498f5083 100644 --- a/toolkit/components/extensions/test/xpcshell/head_remote.js +++ b/toolkit/components/extensions/test/xpcshell/head_remote.js @@ -1,5 +1,4 @@ "use strict"; -/* globals ExtensionTestUtils */ - -ExtensionTestUtils.remoteContentScripts = true; +Services.prefs.setBoolPref("extensions.webextensions.remote", true); +Services.prefs.setIntPref("dom.ipc.keepProcessesAlive.extension", 1); diff --git a/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini b/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini new file mode 100644 index 000000000000..32f73db633d8 --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini @@ -0,0 +1,60 @@ +[test_ext_alarms.js] +[test_ext_alarms_does_not_fire.js] +[test_ext_alarms_periodic.js] +[test_ext_alarms_replaces.js] +[test_ext_background_generated_load_events.js] +[test_ext_background_generated_reload.js] +[test_ext_background_global_history.js] +skip-if = os == "android" # Android does not use Places for history. +[test_ext_background_private_browsing.js] +[test_ext_background_runtime_connect_params.js] +[test_ext_background_sub_windows.js] +[test_ext_background_telemetry.js] +[test_ext_background_window_properties.js] +skip-if = os == "android" +[test_ext_contextual_identities.js] +skip-if = os == "android" # Containers are not exposed to android. +[test_ext_debugging_utils.js] +[test_ext_downloads.js] +[test_ext_downloads_download.js] +skip-if = os == "android" +[test_ext_downloads_misc.js] +skip-if = os == "android" || (os=='linux' && bits==32) # linux32: bug 1324870 +[test_ext_downloads_search.js] +skip-if = os == "android" +[test_ext_experiments.js] +skip-if = release_or_beta +[test_ext_extension.js] +[test_ext_extensionPreferencesManager.js] +[test_ext_extensionSettingsStore.js] +[test_ext_idle.js] +[test_ext_localStorage.js] +[test_ext_management.js] +[test_ext_management_uninstall_self.js] +[test_ext_onmessage_removelistener.js] +skip-if = true # This test no longer tests what it is meant to test. +[test_ext_privacy.js] +[test_ext_privacy_disable.js] +[test_ext_privacy_update.js] +[test_ext_runtime_connect_no_receiver.js] +[test_ext_runtime_getBrowserInfo.js] +[test_ext_runtime_getPlatformInfo.js] +[test_ext_runtime_onInstalled_and_onStartup.js] +[test_ext_runtime_sendMessage.js] +[test_ext_runtime_sendMessage_args.js] +[test_ext_runtime_sendMessage_errors.js] +[test_ext_runtime_sendMessage_no_receiver.js] +[test_ext_runtime_sendMessage_self.js] +[test_ext_shutdown_cleanup.js] +[test_ext_simple.js] +[test_ext_startup_cache.js] +[test_ext_storage.js] +[test_ext_storage_sync.js] +head = head.js head_sync.js +skip-if = os == "android" +[test_ext_storage_sync_crypto.js] +skip-if = os == "android" +[test_ext_topSites.js] +skip-if = os == "android" +[test_native_messaging.js] +skip-if = os == "android" diff --git a/toolkit/components/extensions/test/xpcshell/xpcshell-e10s.ini b/toolkit/components/extensions/test/xpcshell/xpcshell-e10s.ini new file mode 100644 index 000000000000..4c4737bf4758 --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/xpcshell-e10s.ini @@ -0,0 +1,12 @@ +[DEFAULT] +head = head.js head_e10s.js +tail = +firefox-appdir = browser +skip-if = appname == "thunderbird" || os == "android" +dupe-manifest = +support-files = + data/** + xpcshell-content.ini +tags = webextensions webextensions-e10s + +[include:xpcshell-content.ini] diff --git a/toolkit/components/extensions/test/xpcshell/xpcshell-remote.ini b/toolkit/components/extensions/test/xpcshell/xpcshell-remote.ini index 7a0452810026..8af28a476edc 100644 --- a/toolkit/components/extensions/test/xpcshell/xpcshell-remote.ini +++ b/toolkit/components/extensions/test/xpcshell/xpcshell-remote.ini @@ -1,5 +1,5 @@ [DEFAULT] -head = head.js head_remote.js +head = head.js head_remote.js head_e10s.js tail = firefox-appdir = browser skip-if = appname == "thunderbird" || os == "android" @@ -7,6 +7,7 @@ dupe-manifest = support-files = data/** xpcshell-content.ini -tags = webextensions webextensions-e10s +tags = webextensions remote-webextensions +[include:xpcshell-common.ini] [include:xpcshell-content.ini] diff --git a/toolkit/components/extensions/test/xpcshell/xpcshell.ini b/toolkit/components/extensions/test/xpcshell/xpcshell.ini index 14e1562a5ba7..7a0201784dd8 100644 --- a/toolkit/components/extensions/test/xpcshell/xpcshell.ini +++ b/toolkit/components/extensions/test/xpcshell/xpcshell.ini @@ -7,87 +7,48 @@ support-files = data/** head_sync.js xpcshell-content.ini -tags = webextensions +tags = webextensions in-process-webextensions + +# This file contains tests which are not affected by multi-process +# configuration, or do not support out-of-process content or extensions +# for one reason or another. +# +# Tests which are affected by remote content or remote extensions should +# go in one of: +# +# - xpcshell-common.ini +# For tests which should run in all configurations. +# - xpcshell-remote.ini +# For tests which should only run with both remote extensions and remote content. +# - xpcshell-content.ini +# For tests which rely on content pages, and should run in all configurations. +# - xpcshell-e10s.ini +# For tests which rely on conetn pages, and should only run with remote content +# but in-process extensions. [test_csp_custom_policies.js] [test_csp_validator.js] -[test_ext_alarms.js] -[test_ext_alarms_does_not_fire.js] -[test_ext_alarms_periodic.js] -[test_ext_alarms_replaces.js] -[test_ext_api_permissions.js] -[test_ext_background_generated_load_events.js] -[test_ext_background_generated_reload.js] -[test_ext_background_global_history.js] -skip-if = os == "android" # Android does not use Places for history. -[test_ext_background_private_browsing.js] -[test_ext_background_runtime_connect_params.js] -[test_ext_background_sub_windows.js] -[test_ext_background_telemetry.js] -[test_ext_background_window_properties.js] -skip-if = os == "android" [test_ext_contexts.js] -[test_ext_contextual_identities.js] -skip-if = os == "android" # Containers are not exposed to android. -[test_ext_debugging_utils.js] -[test_ext_downloads.js] -[test_ext_downloads_download.js] -skip-if = os == "android" -[test_ext_downloads_misc.js] -skip-if = os == "android" || (os=='linux' && bits==32) # linux32: bug 1324870 -[test_ext_downloads_search.js] -skip-if = os == "android" -[test_ext_experiments.js] -skip-if = release_or_beta -[test_ext_extension.js] -[test_ext_extensionPreferencesManager.js] -[test_ext_extensionSettingsStore.js] -[test_ext_idle.js] [test_ext_json_parser.js] -[test_ext_localStorage.js] -[test_ext_management.js] -[test_ext_management_uninstall_self.js] [test_ext_manifest_content_security_policy.js] [test_ext_manifest_incognito.js] [test_ext_manifest_minimum_chrome_version.js] [test_ext_manifest_themes.js] -[test_ext_onmessage_removelistener.js] -skip-if = true # This test no longer tests what it is meant to test. -[test_ext_permissions.js] -skip-if = os == "android" # Bug 1350559 -[test_ext_privacy.js] -[test_ext_privacy_disable.js] -[test_ext_privacy_update.js] -[test_ext_runtime_connect_no_receiver.js] -[test_ext_runtime_getBrowserInfo.js] -[test_ext_runtime_getPlatformInfo.js] -[test_ext_runtime_onInstalled_and_onStartup.js] -[test_ext_runtime_sendMessage.js] -[test_ext_runtime_sendMessage_args.js] -[test_ext_runtime_sendMessage_errors.js] -[test_ext_runtime_sendMessage_no_receiver.js] -[test_ext_runtime_sendMessage_self.js] [test_ext_schemas.js] [test_ext_schemas_async.js] [test_ext_schemas_allowed_contexts.js] [test_ext_schemas_revoke.js] -[test_ext_shutdown_cleanup.js] -[test_ext_simple.js] -[test_ext_startup_cache.js] -[test_ext_storage.js] -[test_ext_storage_sync.js] -head = head.js head_sync.js -skip-if = os == "android" -[test_ext_storage_sync_crypto.js] -skip-if = os == "android" [test_ext_themes_supported_properties.js] -[test_ext_topSites.js] -skip-if = os == "android" -[test_ext_legacy_extension_context.js] -[test_ext_legacy_extension_embedding.js] [test_locale_converter.js] [test_locale_data.js] -[test_native_messaging.js] -skip-if = os == "android" + +[test_ext_permissions.js] +skip-if = os == "android" # Bug 1350559 +[test_ext_api_permissions.js] + +[test_ext_legacy_extension_context.js] +[test_ext_legacy_extension_embedding.js] [test_proxy_scripts.js] + +[include:xpcshell-common.ini] [include:xpcshell-content.ini] diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index a05b63196ce2..04c8c68c228d 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -1650,38 +1650,6 @@ "n_buckets": 20, "description": "Maximum retention time for the gradient cache. (ms)" }, - "WORD_CACHE_HITS_CONTENT": { - "record_in_processes": ["main", "content"], - "expires_in_version": "never", - "kind": "exponential", - "high": 256, - "n_buckets": 30, - "description": "Word cache hits, content text (chars)" - }, - "WORD_CACHE_HITS_CHROME": { - "record_in_processes": ["main", "content"], - "expires_in_version": "never", - "kind": "exponential", - "high": 256, - "n_buckets": 30, - "description": "Word cache hits, chrome text (chars)" - }, - "WORD_CACHE_MISSES_CONTENT": { - "record_in_processes": ["main", "content"], - "expires_in_version": "never", - "kind": "exponential", - "high": 256, - "n_buckets": 30, - "description": "Word cache misses, content text (chars)" - }, - "WORD_CACHE_MISSES_CHROME": { - "record_in_processes": ["main", "content"], - "expires_in_version": "never", - "kind": "exponential", - "high": 256, - "n_buckets": 30, - "description": "Word cache misses, chrome text (chars)" - }, "FONT_CACHE_HIT": { "record_in_processes": ["main", "content"], "expires_in_version": "never", diff --git a/toolkit/components/telemetry/Telemetry.h b/toolkit/components/telemetry/Telemetry.h index 5cafa9935054..88b25fb76b6e 100644 --- a/toolkit/components/telemetry/Telemetry.h +++ b/toolkit/components/telemetry/Telemetry.h @@ -191,6 +191,7 @@ public: : start(aStart) , key(aKey) { + MOZ_ASSERT(!aKey.IsEmpty(), "The key must not be empty."); MOZ_GUARD_OBJECT_NOTIFIER_INIT; } diff --git a/toolkit/components/telemetry/histogram-whitelists.json b/toolkit/components/telemetry/histogram-whitelists.json index 1c99e528feba..f178c5379978 100644 --- a/toolkit/components/telemetry/histogram-whitelists.json +++ b/toolkit/components/telemetry/histogram-whitelists.json @@ -626,10 +626,6 @@ "WEBCRYPTO_METHOD", "WEBCRYPTO_RESOLVED", "WEBSOCKETS_HANDSHAKE_TYPE", - "WORD_CACHE_HITS_CHROME", - "WORD_CACHE_HITS_CONTENT", - "WORD_CACHE_MISSES_CHROME", - "WORD_CACHE_MISSES_CONTENT", "XMLHTTPREQUEST_ASYNC_OR_SYNC", "XUL_CACHE_DISABLED" ], @@ -1603,10 +1599,6 @@ "WEBRTC_VIDEO_RECOVERY_AFTER_ERROR_PER_MIN", "WEBRTC_VIDEO_RECOVERY_BEFORE_ERROR_PER_MIN", "WEBSOCKETS_HANDSHAKE_TYPE", - "WORD_CACHE_HITS_CHROME", - "WORD_CACHE_HITS_CONTENT", - "WORD_CACHE_MISSES_CHROME", - "WORD_CACHE_MISSES_CONTENT", "XMLHTTPREQUEST_ASYNC_OR_SYNC", "XUL_CACHE_DISABLED" ], diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index a50e1194ed5b..61908c20ad17 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -369,6 +369,9 @@ static const int32_t kResizableBorderMinSize = 3; // Cached pointer events enabler value, True if pointer events are enabled. static bool gIsPointerEventsEnabled = false; +// True if we should use compositing for popup widgets. +static bool gIsPopupCompositingEnabled = false; + // We should never really try to accelerate windows bigger than this. In some // cases this might lead to no D3D9 acceleration where we could have had it // but D3D9 does not reliably report when it supports bigger windows. 8192 @@ -671,6 +674,10 @@ nsWindow::nsWindow() Preferences::AddBoolVarCache(&gIsPointerEventsEnabled, "dom.w3c_pointer_events.enabled", gIsPointerEventsEnabled); + + Preferences::AddBoolVarCache(&gIsPopupCompositingEnabled, + "layers.popups.compositing.enabled", + gIsPopupCompositingEnabled); } // !sInstanceCount mIdleService = nullptr; @@ -7169,7 +7176,7 @@ nsWindow::ShouldUseOffMainThreadCompositing() // We don't currently support using an accelerated layer manager with // transparent windows so don't even try. I'm also not sure if we even // want to support this case. See bug 593471 - if (mTransparencyMode == eTransparencyTransparent) { + if (!(HasRemoteContent() && gIsPopupCompositingEnabled) && mTransparencyMode == eTransparencyTransparent) { return false; } diff --git a/xpcom/threads/ThrottledEventQueue.cpp b/xpcom/threads/ThrottledEventQueue.cpp index 98939d1f4cfa..db8925a84442 100644 --- a/xpcom/threads/ThrottledEventQueue.cpp +++ b/xpcom/threads/ThrottledEventQueue.cpp @@ -96,9 +96,6 @@ class ThrottledEventQueue::Inner final : public nsIObserver // any thread, protected by mutex nsCOMPtr mExecutor; - // any thread, atomic - Atomic mExecutionDepth; - // any thread, protected by mutex bool mShutdownStarted; @@ -108,7 +105,6 @@ class ThrottledEventQueue::Inner final : public nsIObserver , mEventsAvailable(mMutex, "[ThrottledEventQueue::Inner.mEventsAvailable]") , mEventQueue(mEventsAvailable, nsEventQueue::eNormalQueue) , mBaseTarget(aBaseTarget) - , mExecutionDepth(0) , mShutdownStarted(false) { } @@ -139,12 +135,7 @@ class ThrottledEventQueue::Inner final : public nsIObserver } if (nsCOMPtr named = do_QueryInterface(event)) { - // Increase mExecutionDepth here so that GetName is allowed to call - // IsOnCurrentThread on us and have it be true (in the case when we are on - // the right thread). - mExecutionDepth++; nsresult rv = named->GetName(aName); - mExecutionDepth--; return rv; } @@ -196,9 +187,7 @@ class ThrottledEventQueue::Inner final : public nsIObserver } // Execute the event now that we have unlocked. - ++mExecutionDepth; Unused << event->Run(); - --mExecutionDepth; // If shutdown was started and the queue is now empty we can now // finalize the shutdown. This is performed separately at the end @@ -377,27 +366,7 @@ public: nsresult IsOnCurrentThread(bool* aResult) { - // Any thread - - bool shutdownAndIdle = false; - { - MutexAutoLock lock(mMutex); - shutdownAndIdle = mShutdownStarted && mEventQueue.Count(lock) == 0; - } - - bool onBaseTarget = false; - nsresult rv = mBaseTarget->IsOnCurrentThread(&onBaseTarget); - if (NS_FAILED(rv)) { - return rv; - } - - // We consider the current stack on this event target if are on - // the base target and one of the following is true - // 1) We are currently running an event OR - // 2) We are both shutting down and the queue is idle - *aResult = onBaseTarget && (mExecutionDepth || shutdownAndIdle); - - return NS_OK; + return mBaseTarget->IsOnCurrentThread(aResult); } NS_DECL_THREADSAFE_ISUPPORTS diff --git a/xpcom/threads/nsThread.cpp b/xpcom/threads/nsThread.cpp index 2c5a18651a85..bf7980cca45d 100644 --- a/xpcom/threads/nsThread.cpp +++ b/xpcom/threads/nsThread.cpp @@ -1046,7 +1046,8 @@ nsThread::HasPendingEvents(bool* aResult) { MutexAutoLock lock(mLock); - *aResult = mEvents->HasPendingEvent(lock); + *aResult = mEvents->HasPendingEvent(lock) || + mIdleEvents.HasPendingEvent(lock); } return NS_OK; }