зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to mozilla-central r=merge a=merge
This commit is contained in:
Коммит
550148ab69
|
@ -1330,9 +1330,9 @@ imply_option('RUSTC_OPT_LEVEL', '2', when='--enable-release')
|
||||||
|
|
||||||
|
|
||||||
@depends('RUSTC_OPT_LEVEL', debug_rust, '--enable-debug-symbols',
|
@depends('RUSTC_OPT_LEVEL', debug_rust, '--enable-debug-symbols',
|
||||||
moz_optimize)
|
moz_optimize, host, target)
|
||||||
def rust_compiler_flags(opt_level_option, debug_rust, debug_symbols,
|
def rust_compiler_flags(opt_level_option, debug_rust, debug_symbols,
|
||||||
moz_optimize):
|
moz_optimize, host, target):
|
||||||
optimize = moz_optimize.optimize
|
optimize = moz_optimize.optimize
|
||||||
|
|
||||||
# Cargo currently supports only two interesting profiles for building:
|
# Cargo currently supports only two interesting profiles for building:
|
||||||
|
@ -1362,7 +1362,11 @@ def rust_compiler_flags(opt_level_option, debug_rust, debug_symbols,
|
||||||
debug_assertions = False
|
debug_assertions = False
|
||||||
|
|
||||||
if debug_symbols:
|
if debug_symbols:
|
||||||
debug_info = '2'
|
if host.kernel == 'Linux' and target.kernel == 'Darwin':
|
||||||
|
# hack to work around dsymutil failing on cross-OSX builds (bug 1410148)
|
||||||
|
debug_info = '1'
|
||||||
|
else:
|
||||||
|
debug_info = '2'
|
||||||
|
|
||||||
opts = []
|
opts = []
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
This is the debugger.html project output.
|
This is the debugger.html project output.
|
||||||
See https://github.com/devtools-html/debugger.html
|
See https://github.com/devtools-html/debugger.html
|
||||||
|
|
||||||
Taken from upstream commit: 09d6d4f93135367b2639d78ad884434f73ab449c
|
Taken from upstream commit: d2e91e574acbe3d5b546508d028bd278eaabd286
|
||||||
|
|
||||||
Packages:
|
Packages:
|
||||||
- babel-plugin-transform-es2015-modules-commonjs @6.26.0
|
- babel-plugin-transform-es2015-modules-commonjs @6.26.0
|
||||||
|
|
|
@ -604,6 +604,30 @@ button:focus {
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.d-flex {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-items-center {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rounded-circle {
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-white {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.min-width-0 {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/>. */
|
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||||
|
@ -2875,6 +2899,22 @@ html .breakpoints-list .breakpoint.paused {
|
||||||
border-top: 1px solid var(--theme-splitter-color);
|
border-top: 1px solid var(--theme-splitter-color);
|
||||||
border-bottom: 1px solid var(--theme-splitter-color);
|
border-bottom: 1px solid var(--theme-splitter-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.frames ul .frames-group.expanded .badge {
|
||||||
|
color: var(--theme-highlight-blue);
|
||||||
|
}
|
||||||
|
.badge {
|
||||||
|
--size: 17px;
|
||||||
|
--radius: calc(var(--size) / 2);
|
||||||
|
height: var(--size);
|
||||||
|
min-width: var(--size);
|
||||||
|
line-height: var(--size);
|
||||||
|
background: var(--theme-toolbar-background-hover);
|
||||||
|
color: var(--theme-body-color);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
padding: 0 4px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/>. */
|
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||||
|
@ -2918,7 +2958,7 @@ html .breakpoints-list .breakpoint.paused {
|
||||||
}
|
}
|
||||||
|
|
||||||
.frames ul li {
|
.frames ul li {
|
||||||
padding: 7px 10px 7px 21px;
|
padding: 0 10px 0 21px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
@ -2932,6 +2972,11 @@ html .breakpoints-list .breakpoint.paused {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.frames .badge {
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.frames .location {
|
.frames .location {
|
||||||
font-weight: lighter;
|
font-weight: lighter;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -2939,6 +2984,7 @@ html .breakpoints-list .breakpoint.paused {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-light .frames .location,
|
.theme-light .frames .location,
|
||||||
|
@ -2954,7 +3000,7 @@ html .breakpoints-list .breakpoint.paused {
|
||||||
.frames .title {
|
.frames .title {
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-right: 1em;
|
margin: 7px 0.5em 7px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.frames ul li:hover,
|
.frames ul li:hover,
|
||||||
|
@ -3594,7 +3640,7 @@ html .welcomebox .toggle-button-end.collapsed {
|
||||||
top: 23px;
|
top: 23px;
|
||||||
width: var(--width);
|
width: var(--width);
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
overflow: scroll;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
html[dir="rtl"] .dropdown {
|
html[dir="rtl"] .dropdown {
|
||||||
|
|
|
@ -26986,7 +26986,7 @@ Object.defineProperty(exports, "__esModule", {
|
||||||
});
|
});
|
||||||
exports.default = FrameMenu;
|
exports.default = FrameMenu;
|
||||||
|
|
||||||
var _devtoolsLaunchpad = __webpack_require__(1362);
|
var _devtoolsContextmenu = __webpack_require__(1413);
|
||||||
|
|
||||||
var _clipboard = __webpack_require__(1388);
|
var _clipboard = __webpack_require__(1388);
|
||||||
|
|
||||||
|
@ -27052,7 +27052,7 @@ function FrameMenu(frame, frameworkGroupingOn, callbacks, event) {
|
||||||
|
|
||||||
menuOptions.push(copyStackTraceItem);
|
menuOptions.push(copyStackTraceItem);
|
||||||
|
|
||||||
(0, _devtoolsLaunchpad.showMenu)(event, menuOptions);
|
(0, _devtoolsContextmenu.showMenu)(event, menuOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
@ -35317,7 +35317,7 @@ var _sourcesTree = __webpack_require__(1442);
|
||||||
|
|
||||||
var _immutable = __webpack_require__(146);
|
var _immutable = __webpack_require__(146);
|
||||||
|
|
||||||
var _devtoolsLaunchpad = __webpack_require__(1362);
|
var _devtoolsContextmenu = __webpack_require__(1413);
|
||||||
|
|
||||||
var _clipboard = __webpack_require__(1388);
|
var _clipboard = __webpack_require__(1388);
|
||||||
|
|
||||||
|
@ -35495,7 +35495,7 @@ class SourcesTree extends _react.Component {
|
||||||
click: () => (0, _ui.setProjectDirectoryRoot)(item.path)
|
click: () => (0, _ui.setProjectDirectoryRoot)(item.path)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
(0, _devtoolsLaunchpad.showMenu)(event, menuOptions);
|
(0, _devtoolsContextmenu.showMenu)(event, menuOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderItem(item, depth, focused, _, expanded, { setExpanded }) {
|
renderItem(item, depth, focused, _, expanded, { setExpanded }) {
|
||||||
|
@ -40608,7 +40608,7 @@ exports.gutterMenu = gutterMenu;
|
||||||
|
|
||||||
var _react = __webpack_require__(0);
|
var _react = __webpack_require__(0);
|
||||||
|
|
||||||
var _devtoolsLaunchpad = __webpack_require__(1362);
|
var _devtoolsContextmenu = __webpack_require__(1413);
|
||||||
|
|
||||||
var _redux = __webpack_require__(3);
|
var _redux = __webpack_require__(3);
|
||||||
|
|
||||||
|
@ -40709,7 +40709,7 @@ function gutterMenu({
|
||||||
items.push(disableBreakpoint);
|
items.push(disableBreakpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
(0, _devtoolsLaunchpad.showMenu)(event, items);
|
(0, _devtoolsContextmenu.showMenu)(event, items);
|
||||||
}
|
}
|
||||||
|
|
||||||
class GutterContextMenuComponent extends _react.PureComponent {
|
class GutterContextMenuComponent extends _react.PureComponent {
|
||||||
|
@ -40773,7 +40773,7 @@ Object.defineProperty(exports, "__esModule", {
|
||||||
|
|
||||||
var _react = __webpack_require__(0);
|
var _react = __webpack_require__(0);
|
||||||
|
|
||||||
var _devtoolsLaunchpad = __webpack_require__(1362);
|
var _devtoolsContextmenu = __webpack_require__(1413);
|
||||||
|
|
||||||
var _devtoolsSourceMap = __webpack_require__(1360);
|
var _devtoolsSourceMap = __webpack_require__(1360);
|
||||||
|
|
||||||
|
@ -40858,6 +40858,7 @@ function getMenuItems(event, {
|
||||||
const isPrettyPrinted = (0, _source.isPretty)(selectedSource.toJS());
|
const isPrettyPrinted = (0, _source.isPretty)(selectedSource.toJS());
|
||||||
|
|
||||||
const jumpLabel = {
|
const jumpLabel = {
|
||||||
|
id: "node-menu-jump",
|
||||||
accesskey: L10N.getStr("editor.jumpToMappedLocation1.accesskey"),
|
accesskey: L10N.getStr("editor.jumpToMappedLocation1.accesskey"),
|
||||||
disabled: _devtoolsSourceMap.isGeneratedId && !hasSourceMap,
|
disabled: _devtoolsSourceMap.isGeneratedId && !hasSourceMap,
|
||||||
label: L10N.getFormatStr("editor.jumpToMappedLocation1", isOriginal ? L10N.getStr("generated") : L10N.getStr("original")),
|
label: L10N.getFormatStr("editor.jumpToMappedLocation1", isOriginal ? L10N.getStr("generated") : L10N.getStr("original")),
|
||||||
|
@ -40865,6 +40866,7 @@ function getMenuItems(event, {
|
||||||
};
|
};
|
||||||
|
|
||||||
const watchExpressionLabel = {
|
const watchExpressionLabel = {
|
||||||
|
id: "node-menu-add-watch-expression",
|
||||||
accesskey: L10N.getStr("expressions.accesskey"),
|
accesskey: L10N.getStr("expressions.accesskey"),
|
||||||
label: L10N.getStr("expressions.label"),
|
label: L10N.getStr("expressions.label"),
|
||||||
click: () => addExpression(editor.codeMirror.getSelection())
|
click: () => addExpression(editor.codeMirror.getSelection())
|
||||||
|
@ -40935,7 +40937,7 @@ class EditorMenu extends _react.PureComponent {
|
||||||
const { contextMenu } = nextProps,
|
const { contextMenu } = nextProps,
|
||||||
options = _objectWithoutProperties(nextProps, ["contextMenu"]);
|
options = _objectWithoutProperties(nextProps, ["contextMenu"]);
|
||||||
const { event } = contextMenu;
|
const { event } = contextMenu;
|
||||||
(0, _devtoolsLaunchpad.showMenu)(event, getMenuItems(event, options));
|
(0, _devtoolsContextmenu.showMenu)(event, getMenuItems(event, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -41174,10 +41176,6 @@ var _redux = __webpack_require__(3);
|
||||||
|
|
||||||
var _prefs = __webpack_require__(226);
|
var _prefs = __webpack_require__(226);
|
||||||
|
|
||||||
var _reactImmutableProptypes = __webpack_require__(150);
|
|
||||||
|
|
||||||
var _reactImmutableProptypes2 = _interopRequireDefault(_reactImmutableProptypes);
|
|
||||||
|
|
||||||
var _actions = __webpack_require__(1354);
|
var _actions = __webpack_require__(1354);
|
||||||
|
|
||||||
var _actions2 = _interopRequireDefault(_actions);
|
var _actions2 = _interopRequireDefault(_actions);
|
||||||
|
@ -41238,11 +41236,9 @@ __webpack_require__(1342);
|
||||||
|
|
||||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||||
|
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
const Scopes = (0, _devtoolsConfig.isEnabled)("chromeScopes") ? _ChromeScopes2.default : _Scopes3.default; /* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/>. */
|
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||||
|
|
||||||
const Scopes = (0, _devtoolsConfig.isEnabled)("chromeScopes") ? _ChromeScopes2.default : _Scopes3.default;
|
|
||||||
|
|
||||||
function debugBtn(onClick, type, className, tooltip) {
|
function debugBtn(onClick, type, className, tooltip) {
|
||||||
return _react2.default.createElement(
|
return _react2.default.createElement(
|
||||||
|
@ -41433,7 +41429,7 @@ SecondaryPanes.propTypes = {
|
||||||
evaluateExpressions: _propTypes2.default.func.isRequired,
|
evaluateExpressions: _propTypes2.default.func.isRequired,
|
||||||
pauseData: _propTypes2.default.object,
|
pauseData: _propTypes2.default.object,
|
||||||
horizontal: _propTypes2.default.bool,
|
horizontal: _propTypes2.default.bool,
|
||||||
breakpoints: _reactImmutableProptypes2.default.map.isRequired,
|
breakpoints: _propTypes2.default.object,
|
||||||
breakpointsDisabled: _propTypes2.default.bool,
|
breakpointsDisabled: _propTypes2.default.bool,
|
||||||
breakpointsLoading: _propTypes2.default.bool,
|
breakpointsLoading: _propTypes2.default.bool,
|
||||||
toggleAllBreakpoints: _propTypes2.default.func.isRequired,
|
toggleAllBreakpoints: _propTypes2.default.func.isRequired,
|
||||||
|
@ -41498,7 +41494,7 @@ var _utils = __webpack_require__(1366);
|
||||||
|
|
||||||
var _source = __webpack_require__(1356);
|
var _source = __webpack_require__(1356);
|
||||||
|
|
||||||
var _devtoolsLaunchpad = __webpack_require__(1362);
|
var _devtoolsContextmenu = __webpack_require__(1413);
|
||||||
|
|
||||||
var _Close = __webpack_require__(1374);
|
var _Close = __webpack_require__(1374);
|
||||||
|
|
||||||
|
@ -41733,7 +41729,7 @@ class Breakpoints extends _react.PureComponent {
|
||||||
hidden: () => !breakpoint.condition
|
hidden: () => !breakpoint.condition
|
||||||
}];
|
}];
|
||||||
|
|
||||||
(0, _devtoolsLaunchpad.showMenu)(e, (0, _devtoolsLaunchpad.buildMenu)(items));
|
(0, _devtoolsContextmenu.showMenu)(e, (0, _devtoolsContextmenu.buildMenu)(items));
|
||||||
}
|
}
|
||||||
|
|
||||||
selectBreakpoint(breakpoint) {
|
selectBreakpoint(breakpoint) {
|
||||||
|
@ -42301,8 +42297,16 @@ var _Frame = __webpack_require__(1453);
|
||||||
|
|
||||||
var _Frame2 = _interopRequireDefault(_Frame);
|
var _Frame2 = _interopRequireDefault(_Frame);
|
||||||
|
|
||||||
|
var _Badge = __webpack_require__(1704);
|
||||||
|
|
||||||
|
var _Badge2 = _interopRequireDefault(_Badge);
|
||||||
|
|
||||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||||
|
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||||
|
|
||||||
function FrameLocation({ frame }) {
|
function FrameLocation({ frame }) {
|
||||||
const library = (0, _frame.getLibraryFromUrl)(frame);
|
const library = (0, _frame.getLibraryFromUrl)(frame);
|
||||||
if (!library) {
|
if (!library) {
|
||||||
|
@ -42315,9 +42319,7 @@ function FrameLocation({ frame }) {
|
||||||
library,
|
library,
|
||||||
_react2.default.createElement(_Svg2.default, { name: library.toLowerCase(), className: "annotation-logo" })
|
_react2.default.createElement(_Svg2.default, { name: library.toLowerCase(), className: "annotation-logo" })
|
||||||
);
|
);
|
||||||
} /* This Source Code Form is subject to the terms of the Mozilla Public
|
}
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
|
||||||
|
|
||||||
FrameLocation.displayName = "FrameLocation";
|
FrameLocation.displayName = "FrameLocation";
|
||||||
|
|
||||||
|
@ -42392,8 +42394,17 @@ class Group extends _react.Component {
|
||||||
},
|
},
|
||||||
_react2.default.createElement(
|
_react2.default.createElement(
|
||||||
"div",
|
"div",
|
||||||
{ className: "title" },
|
{ className: "d-flex align-items-center min-width-0" },
|
||||||
displayName
|
_react2.default.createElement(
|
||||||
|
"div",
|
||||||
|
{ className: "title" },
|
||||||
|
displayName
|
||||||
|
),
|
||||||
|
_react2.default.createElement(
|
||||||
|
_Badge2.default,
|
||||||
|
null,
|
||||||
|
this.props.group.length
|
||||||
|
)
|
||||||
),
|
),
|
||||||
_react2.default.createElement(FrameLocation, { frame: frame })
|
_react2.default.createElement(FrameLocation, { frame: frame })
|
||||||
);
|
);
|
||||||
|
@ -43775,7 +43786,7 @@ var _Close = __webpack_require__(1374);
|
||||||
|
|
||||||
var _Close2 = _interopRequireDefault(_Close);
|
var _Close2 = _interopRequireDefault(_Close);
|
||||||
|
|
||||||
var _devtoolsLaunchpad = __webpack_require__(1362);
|
var _devtoolsContextmenu = __webpack_require__(1413);
|
||||||
|
|
||||||
var _lodash = __webpack_require__(2);
|
var _lodash = __webpack_require__(2);
|
||||||
|
|
||||||
|
@ -43993,7 +44004,7 @@ class SourceTabs extends _react.PureComponent {
|
||||||
items.push({ item: prettyPrint });
|
items.push({ item: prettyPrint });
|
||||||
}
|
}
|
||||||
|
|
||||||
(0, _devtoolsLaunchpad.showMenu)(e, (0, _devtoolsLaunchpad.buildMenu)(items));
|
(0, _devtoolsContextmenu.showMenu)(e, (0, _devtoolsContextmenu.buildMenu)(items));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -45938,6 +45949,79 @@ function timing(store) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
/* 1664 */,
|
||||||
|
/* 1665 */,
|
||||||
|
/* 1666 */,
|
||||||
|
/* 1667 */,
|
||||||
|
/* 1668 */,
|
||||||
|
/* 1669 */,
|
||||||
|
/* 1670 */,
|
||||||
|
/* 1671 */,
|
||||||
|
/* 1672 */,
|
||||||
|
/* 1673 */,
|
||||||
|
/* 1674 */,
|
||||||
|
/* 1675 */,
|
||||||
|
/* 1676 */,
|
||||||
|
/* 1677 */,
|
||||||
|
/* 1678 */,
|
||||||
|
/* 1679 */,
|
||||||
|
/* 1680 */,
|
||||||
|
/* 1681 */,
|
||||||
|
/* 1682 */,
|
||||||
|
/* 1683 */,
|
||||||
|
/* 1684 */,
|
||||||
|
/* 1685 */,
|
||||||
|
/* 1686 */,
|
||||||
|
/* 1687 */,
|
||||||
|
/* 1688 */,
|
||||||
|
/* 1689 */,
|
||||||
|
/* 1690 */,
|
||||||
|
/* 1691 */,
|
||||||
|
/* 1692 */,
|
||||||
|
/* 1693 */,
|
||||||
|
/* 1694 */,
|
||||||
|
/* 1695 */,
|
||||||
|
/* 1696 */,
|
||||||
|
/* 1697 */,
|
||||||
|
/* 1698 */,
|
||||||
|
/* 1699 */,
|
||||||
|
/* 1700 */,
|
||||||
|
/* 1701 */,
|
||||||
|
/* 1702 */,
|
||||||
|
/* 1703 */,
|
||||||
|
/* 1704 */
|
||||||
|
/***/ (function(module, exports, __webpack_require__) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
|
||||||
|
var _react = __webpack_require__(0);
|
||||||
|
|
||||||
|
var _react2 = _interopRequireDefault(_react);
|
||||||
|
|
||||||
|
__webpack_require__(1705);
|
||||||
|
|
||||||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||||
|
|
||||||
|
const Badge = ({ children }) => _react2.default.createElement(
|
||||||
|
"div",
|
||||||
|
{ className: "badge text-white text-center" },
|
||||||
|
children
|
||||||
|
);
|
||||||
|
|
||||||
|
exports.default = Badge;
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
/* 1705 */
|
||||||
|
/***/ (function(module, exports) {
|
||||||
|
|
||||||
|
// removed by extract-text-webpack-plugin
|
||||||
|
|
||||||
/***/ })
|
/***/ })
|
||||||
/******/ ]);
|
/******/ ]);
|
||||||
});
|
});
|
|
@ -147,6 +147,10 @@ class ToolboxController extends Component {
|
||||||
this.updateButtonIds();
|
this.updateButtonIds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get panelDefinitions() {
|
||||||
|
return this.state.panelDefinitions;
|
||||||
|
}
|
||||||
|
|
||||||
setToolboxButtons(toolboxButtons) {
|
setToolboxButtons(toolboxButtons) {
|
||||||
// Listen for updates of the checked attribute.
|
// Listen for updates of the checked attribute.
|
||||||
this.state.toolboxButtons.forEach(button => {
|
this.state.toolboxButtons.forEach(button => {
|
||||||
|
|
|
@ -1925,12 +1925,11 @@ Toolbox.prototype = {
|
||||||
* Loads the tool next to the currently selected tool.
|
* Loads the tool next to the currently selected tool.
|
||||||
*/
|
*/
|
||||||
selectNextTool: function () {
|
selectNextTool: function () {
|
||||||
const index = this.panelDefinitions.findIndex(({id}) => id === this.currentToolId);
|
let definitions = this.component.panelDefinitions;
|
||||||
let definition = this.panelDefinitions[index + 1];
|
const index = definitions.findIndex(({id}) => id === this.currentToolId);
|
||||||
|
let definition = definitions[index + 1];
|
||||||
if (!definition) {
|
if (!definition) {
|
||||||
definition = index === -1
|
definition = index === -1 ? definitions[0] : this.optionsDefinition;
|
||||||
? this.panelDefinitions[0]
|
|
||||||
: this.optionsDefinition;
|
|
||||||
}
|
}
|
||||||
return this.selectTool(definition.id);
|
return this.selectTool(definition.id);
|
||||||
},
|
},
|
||||||
|
@ -1939,11 +1938,12 @@ Toolbox.prototype = {
|
||||||
* Loads the tool just left to the currently selected tool.
|
* Loads the tool just left to the currently selected tool.
|
||||||
*/
|
*/
|
||||||
selectPreviousTool: function () {
|
selectPreviousTool: function () {
|
||||||
const index = this.panelDefinitions.findIndex(({id}) => id === this.currentToolId);
|
let definitions = this.component.panelDefinitions;
|
||||||
let definition = this.panelDefinitions[index - 1];
|
const index = definitions.findIndex(({id}) => id === this.currentToolId);
|
||||||
|
let definition = definitions[index - 1];
|
||||||
if (!definition) {
|
if (!definition) {
|
||||||
definition = index === -1
|
definition = index === -1
|
||||||
? this.panelDefinitions[this.panelDefinitions.length - 1]
|
? definitions[definitions.length - 1]
|
||||||
: this.optionsDefinition;
|
: this.optionsDefinition;
|
||||||
}
|
}
|
||||||
return this.selectTool(definition.id);
|
return this.selectTool(definition.id);
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1970,11 +1970,18 @@ KeyframeEffectReadOnly::UpdateEffectSet(EffectSet* aEffectSet) const
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsIFrame* frame = GetAnimationFrame();
|
||||||
if (HasAnimationOfProperty(eCSSProperty_opacity)) {
|
if (HasAnimationOfProperty(eCSSProperty_opacity)) {
|
||||||
effectSet->SetMayHaveOpacityAnimation();
|
effectSet->SetMayHaveOpacityAnimation();
|
||||||
|
if (frame) {
|
||||||
|
frame->SetMayHaveOpacityAnimation();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (HasAnimationOfProperty(eCSSProperty_transform)) {
|
if (HasAnimationOfProperty(eCSSProperty_transform)) {
|
||||||
effectSet->SetMayHaveTransformAnimation();
|
effectSet->SetMayHaveTransformAnimation();
|
||||||
|
if (frame) {
|
||||||
|
frame->SetMayHaveTransformAnimation();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,12 @@ ClientHandle::Activate(PClientManagerChild* aActor)
|
||||||
ActivateThing(static_cast<ClientHandleChild*>(actor));
|
ActivateThing(static_cast<ClientHandleChild*>(actor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ClientHandle::ExecutionReady(const ClientInfo& aClientInfo)
|
||||||
|
{
|
||||||
|
mClientInfo = aClientInfo;
|
||||||
|
}
|
||||||
|
|
||||||
const ClientInfo&
|
const ClientInfo&
|
||||||
ClientHandle::Info() const
|
ClientHandle::Info() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -32,6 +32,7 @@ class PClientManagerChild;
|
||||||
class ClientHandle final : public ClientThing<ClientHandleChild>
|
class ClientHandle final : public ClientThing<ClientHandleChild>
|
||||||
{
|
{
|
||||||
friend class ClientManager;
|
friend class ClientManager;
|
||||||
|
friend class ClientHandleChild;
|
||||||
|
|
||||||
RefPtr<ClientManager> mManager;
|
RefPtr<ClientManager> mManager;
|
||||||
nsCOMPtr<nsISerialEventTarget> mSerialEventTarget;
|
nsCOMPtr<nsISerialEventTarget> mSerialEventTarget;
|
||||||
|
@ -45,6 +46,10 @@ class ClientHandle final : public ClientThing<ClientHandleChild>
|
||||||
already_AddRefed<ClientOpPromise>
|
already_AddRefed<ClientOpPromise>
|
||||||
StartOp(const ClientOpConstructorArgs& aArgs);
|
StartOp(const ClientOpConstructorArgs& aArgs);
|
||||||
|
|
||||||
|
// Private methods called by ClientHandleChild
|
||||||
|
void
|
||||||
|
ExecutionReady(const ClientInfo& aClientInfo);
|
||||||
|
|
||||||
// Private methods called by ClientManager
|
// Private methods called by ClientManager
|
||||||
ClientHandle(ClientManager* aManager,
|
ClientHandle(ClientManager* aManager,
|
||||||
nsISerialEventTarget* aSerialEventTarget,
|
nsISerialEventTarget* aSerialEventTarget,
|
||||||
|
|
|
@ -14,6 +14,15 @@ namespace dom {
|
||||||
|
|
||||||
using mozilla::ipc::IPCResult;
|
using mozilla::ipc::IPCResult;
|
||||||
|
|
||||||
|
IPCResult
|
||||||
|
ClientHandleChild::RecvExecutionReady(const IPCClientInfo& aClientInfo)
|
||||||
|
{
|
||||||
|
if (mHandle) {
|
||||||
|
mHandle->ExecutionReady(ClientInfo(aClientInfo));
|
||||||
|
}
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ClientHandleChild::ActorDestroy(ActorDestroyReason aReason)
|
ClientHandleChild::ActorDestroy(ActorDestroyReason aReason)
|
||||||
{
|
{
|
||||||
|
@ -50,7 +59,7 @@ void
|
||||||
ClientHandleChild::SetOwner(ClientThing<ClientHandleChild>* aThing)
|
ClientHandleChild::SetOwner(ClientThing<ClientHandleChild>* aThing)
|
||||||
{
|
{
|
||||||
MOZ_DIAGNOSTIC_ASSERT(!mHandle);
|
MOZ_DIAGNOSTIC_ASSERT(!mHandle);
|
||||||
mHandle = aThing;
|
mHandle = static_cast<ClientHandle*>(aThing);
|
||||||
MOZ_DIAGNOSTIC_ASSERT(mHandle);
|
MOZ_DIAGNOSTIC_ASSERT(mHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +67,7 @@ void
|
||||||
ClientHandleChild::RevokeOwner(ClientThing<ClientHandleChild>* aThing)
|
ClientHandleChild::RevokeOwner(ClientThing<ClientHandleChild>* aThing)
|
||||||
{
|
{
|
||||||
MOZ_DIAGNOSTIC_ASSERT(mHandle);
|
MOZ_DIAGNOSTIC_ASSERT(mHandle);
|
||||||
MOZ_DIAGNOSTIC_ASSERT(mHandle == aThing);
|
MOZ_DIAGNOSTIC_ASSERT(mHandle == static_cast<ClientHandle*>(aThing));
|
||||||
mHandle = nullptr;
|
mHandle = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,10 +19,13 @@ template <typename ActorType> class ClientThing;
|
||||||
|
|
||||||
class ClientHandleChild final : public PClientHandleChild
|
class ClientHandleChild final : public PClientHandleChild
|
||||||
{
|
{
|
||||||
ClientThing<ClientHandleChild>* mHandle;
|
ClientHandle* mHandle;
|
||||||
bool mTeardownStarted;
|
bool mTeardownStarted;
|
||||||
|
|
||||||
// PClientHandleChild interface
|
// PClientHandleChild interface
|
||||||
|
mozilla::ipc::IPCResult
|
||||||
|
RecvExecutionReady(const IPCClientInfo& aClientInfo) override;
|
||||||
|
|
||||||
void
|
void
|
||||||
ActorDestroy(ActorDestroyReason aReason) override;
|
ActorDestroy(ActorDestroyReason aReason) override;
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,12 @@ union IPCClientState
|
||||||
IPCClientWorkerState;
|
IPCClientWorkerState;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ClientSourceExecutionReadyArgs
|
||||||
|
{
|
||||||
|
nsCString url;
|
||||||
|
FrameType frameType;
|
||||||
|
};
|
||||||
|
|
||||||
struct ClientOpenWindowArgs
|
struct ClientOpenWindowArgs
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "ClientHandle.h"
|
#include "ClientHandle.h"
|
||||||
#include "ClientManagerChild.h"
|
#include "ClientManagerChild.h"
|
||||||
#include "ClientManagerOpChild.h"
|
#include "ClientManagerOpChild.h"
|
||||||
|
#include "ClientPrefs.h"
|
||||||
#include "ClientSource.h"
|
#include "ClientSource.h"
|
||||||
#include "mozilla/dom/WorkerPrivate.h"
|
#include "mozilla/dom/WorkerPrivate.h"
|
||||||
#include "mozilla/dom/workers/bindings/WorkerHolderToken.h"
|
#include "mozilla/dom/workers/bindings/WorkerHolderToken.h"
|
||||||
|
@ -203,6 +204,8 @@ ClientManager::Startup()
|
||||||
#endif
|
#endif
|
||||||
PR_NewThreadPrivateIndex(&sClientManagerThreadLocalIndex, nullptr);
|
PR_NewThreadPrivateIndex(&sClientManagerThreadLocalIndex, nullptr);
|
||||||
MOZ_DIAGNOSTIC_ASSERT(status == PR_SUCCESS);
|
MOZ_DIAGNOSTIC_ASSERT(status == PR_SUCCESS);
|
||||||
|
|
||||||
|
ClientPrefsInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
|
|
@ -101,6 +101,15 @@ ClientManagerParent::DeallocPClientSourceParent(PClientSourceParent* aActor)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IPCResult
|
||||||
|
ClientManagerParent::RecvPClientSourceConstructor(PClientSourceParent* aActor,
|
||||||
|
const ClientSourceConstructorArgs& aArgs)
|
||||||
|
{
|
||||||
|
ClientSourceParent* actor = static_cast<ClientSourceParent*>(aActor);
|
||||||
|
actor->Init();
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
ClientManagerParent::ClientManagerParent()
|
ClientManagerParent::ClientManagerParent()
|
||||||
: mService(ClientManagerService::GetOrCreateInstance())
|
: mService(ClientManagerService::GetOrCreateInstance())
|
||||||
{
|
{
|
||||||
|
|
|
@ -56,6 +56,10 @@ class ClientManagerParent final : public PClientManagerParent
|
||||||
bool
|
bool
|
||||||
DeallocPClientSourceParent(PClientSourceParent* aActor) override;
|
DeallocPClientSourceParent(PClientSourceParent* aActor) override;
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult
|
||||||
|
RecvPClientSourceConstructor(PClientSourceParent* aActor,
|
||||||
|
const ClientSourceConstructorArgs& aArgs) override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ClientManagerParent();
|
ClientManagerParent();
|
||||||
~ClientManagerParent();
|
~ClientManagerParent();
|
||||||
|
|
|
@ -83,24 +83,33 @@ ClientManagerService::GetOrCreateInstance()
|
||||||
return ref.forget();
|
return ref.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
bool
|
||||||
ClientManagerService::AddSource(ClientSourceParent* aSource)
|
ClientManagerService::AddSource(ClientSourceParent* aSource)
|
||||||
{
|
{
|
||||||
AssertIsOnBackgroundThread();
|
AssertIsOnBackgroundThread();
|
||||||
MOZ_ASSERT(aSource);
|
MOZ_ASSERT(aSource);
|
||||||
auto entry = mSourceTable.LookupForAdd(aSource->Info().Id());
|
auto entry = mSourceTable.LookupForAdd(aSource->Info().Id());
|
||||||
MOZ_DIAGNOSTIC_ASSERT(!entry);
|
// Do not permit overwriting an existing ClientSource with the same
|
||||||
|
// UUID. This would allow a spoofed ClientParentSource actor to
|
||||||
|
// intercept postMessage() intended for the real actor.
|
||||||
|
if (NS_WARN_IF(!!entry)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
entry.OrInsert([&] { return aSource; });
|
entry.OrInsert([&] { return aSource; });
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
bool
|
||||||
ClientManagerService::RemoveSource(ClientSourceParent* aSource)
|
ClientManagerService::RemoveSource(ClientSourceParent* aSource)
|
||||||
{
|
{
|
||||||
AssertIsOnBackgroundThread();
|
AssertIsOnBackgroundThread();
|
||||||
MOZ_ASSERT(aSource);
|
MOZ_ASSERT(aSource);
|
||||||
auto entry = mSourceTable.Lookup(aSource->Info().Id());
|
auto entry = mSourceTable.Lookup(aSource->Info().Id());
|
||||||
MOZ_DIAGNOSTIC_ASSERT(entry);
|
if (NS_WARN_IF(!entry)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
entry.Remove();
|
entry.Remove();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientSourceParent*
|
ClientSourceParent*
|
||||||
|
|
|
@ -30,10 +30,10 @@ public:
|
||||||
static already_AddRefed<ClientManagerService>
|
static already_AddRefed<ClientManagerService>
|
||||||
GetOrCreateInstance();
|
GetOrCreateInstance();
|
||||||
|
|
||||||
void
|
bool
|
||||||
AddSource(ClientSourceParent* aSource);
|
AddSource(ClientSourceParent* aSource);
|
||||||
|
|
||||||
void
|
bool
|
||||||
RemoveSource(ClientSourceParent* aSource);
|
RemoveSource(ClientSourceParent* aSource);
|
||||||
|
|
||||||
ClientSourceParent*
|
ClientSourceParent*
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "ClientPrefs.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
bool gDataURLUniqueOpaqueOrigin = false;
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
void
|
||||||
|
ClientPrefsInit()
|
||||||
|
{
|
||||||
|
Preferences::AddBoolVarCache(&gDataURLUniqueOpaqueOrigin,
|
||||||
|
"security.data_uri.unique_opaque_origin",
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ClientPrefsGetDataURLUniqueOpaqueOrigin()
|
||||||
|
{
|
||||||
|
return gDataURLUniqueOpaqueOrigin;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
|
@ -0,0 +1,21 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef _mozilla_dom_ClientPrefs_h
|
||||||
|
#define _mozilla_dom_ClientPrefs_h
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
void
|
||||||
|
ClientPrefsInit();
|
||||||
|
|
||||||
|
bool
|
||||||
|
ClientPrefsGetAllowUniqueOpaqueOrigin();
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // _mozilla_dom_ClientPrefs_h
|
|
@ -9,11 +9,16 @@
|
||||||
#include "ClientManager.h"
|
#include "ClientManager.h"
|
||||||
#include "ClientManagerChild.h"
|
#include "ClientManagerChild.h"
|
||||||
#include "ClientSourceChild.h"
|
#include "ClientSourceChild.h"
|
||||||
|
#include "ClientValidation.h"
|
||||||
#include "mozilla/dom/ClientIPCTypes.h"
|
#include "mozilla/dom/ClientIPCTypes.h"
|
||||||
|
#include "mozilla/dom/WorkerPrivate.h"
|
||||||
|
#include "nsIDocShell.h"
|
||||||
|
#include "nsPIDOMWindow.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace dom {
|
namespace dom {
|
||||||
|
|
||||||
|
using mozilla::dom::workers::WorkerPrivate;
|
||||||
using mozilla::ipc::PrincipalInfo;
|
using mozilla::ipc::PrincipalInfo;
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -29,9 +34,50 @@ ClientSource::Shutdown()
|
||||||
mManager = nullptr;
|
mManager = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ClientSource::ExecutionReady(const ClientSourceExecutionReadyArgs& aArgs)
|
||||||
|
{
|
||||||
|
// Fast fail if we don't understand this particular principal/URL combination.
|
||||||
|
// This can happen since we use MozURL for validation which does not handle
|
||||||
|
// some of the more obscure internal principal/url combinations. Normal
|
||||||
|
// content pages will pass this check.
|
||||||
|
if (NS_WARN_IF(!ClientIsValidCreationURL(mClientInfo.PrincipalInfo(),
|
||||||
|
aArgs.url()))) {
|
||||||
|
Shutdown();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mClientInfo.SetURL(aArgs.url());
|
||||||
|
mClientInfo.SetFrameType(aArgs.frameType());
|
||||||
|
MaybeExecute([aArgs](PClientSourceChild* aActor) {
|
||||||
|
aActor->SendExecutionReady(aArgs);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
WorkerPrivate*
|
||||||
|
ClientSource::GetWorkerPrivate() const
|
||||||
|
{
|
||||||
|
NS_ASSERT_OWNINGTHREAD(ClientSource);
|
||||||
|
if (!mOwner.is<WorkerPrivate*>()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return mOwner.as<WorkerPrivate*>();
|
||||||
|
}
|
||||||
|
|
||||||
|
nsIDocShell*
|
||||||
|
ClientSource::GetDocShell() const
|
||||||
|
{
|
||||||
|
NS_ASSERT_OWNINGTHREAD(ClientSource);
|
||||||
|
if (!mOwner.is<nsCOMPtr<nsIDocShell>>()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return mOwner.as<nsCOMPtr<nsIDocShell>>();
|
||||||
|
}
|
||||||
|
|
||||||
ClientSource::ClientSource(ClientManager* aManager,
|
ClientSource::ClientSource(ClientManager* aManager,
|
||||||
const ClientSourceConstructorArgs& aArgs)
|
const ClientSourceConstructorArgs& aArgs)
|
||||||
: mManager(aManager)
|
: mManager(aManager)
|
||||||
|
, mOwner(AsVariant(Nothing()))
|
||||||
, mClientInfo(aArgs.id(), aArgs.type(), aArgs.principalInfo(), aArgs.creationTime())
|
, mClientInfo(aArgs.id(), aArgs.type(), aArgs.principalInfo(), aArgs.creationTime())
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(mManager);
|
MOZ_ASSERT(mManager);
|
||||||
|
@ -47,6 +93,15 @@ ClientSource::Activate(PClientManagerChild* aActor)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fast fail if we don't understand this particular kind of PrincipalInfo.
|
||||||
|
// This can happen since we use MozURL for validation which does not handle
|
||||||
|
// some of the more obscure internal principal/url combinations. Normal
|
||||||
|
// content pages will pass this check.
|
||||||
|
if (NS_WARN_IF(!ClientIsValidPrincipalInfo(mClientInfo.PrincipalInfo()))) {
|
||||||
|
Shutdown();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ClientSourceConstructorArgs args(mClientInfo.Id(), mClientInfo.Type(),
|
ClientSourceConstructorArgs args(mClientInfo.Id(), mClientInfo.Type(),
|
||||||
mClientInfo.PrincipalInfo(),
|
mClientInfo.PrincipalInfo(),
|
||||||
mClientInfo.CreationTime());
|
mClientInfo.CreationTime());
|
||||||
|
@ -64,5 +119,118 @@ ClientSource::~ClientSource()
|
||||||
Shutdown();
|
Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsPIDOMWindowInner*
|
||||||
|
ClientSource::GetInnerWindow() const
|
||||||
|
{
|
||||||
|
NS_ASSERT_OWNINGTHREAD(ClientSource);
|
||||||
|
if (!mOwner.is<RefPtr<nsPIDOMWindowInner>>()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return mOwner.as<RefPtr<nsPIDOMWindowInner>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ClientSource::WorkerExecutionReady(WorkerPrivate* aWorkerPrivate)
|
||||||
|
{
|
||||||
|
MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate);
|
||||||
|
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||||
|
|
||||||
|
// Its safe to store the WorkerPrivate* here because the ClientSource
|
||||||
|
// is explicitly destroyed by WorkerPrivate before exiting its run loop.
|
||||||
|
MOZ_DIAGNOSTIC_ASSERT(mOwner.is<Nothing>());
|
||||||
|
mOwner = AsVariant(aWorkerPrivate);
|
||||||
|
|
||||||
|
ClientSourceExecutionReadyArgs args(
|
||||||
|
aWorkerPrivate->GetLocationInfo().mHref,
|
||||||
|
FrameType::None);
|
||||||
|
|
||||||
|
ExecutionReady(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
ClientSource::WindowExecutionReady(nsPIDOMWindowInner* aInnerWindow)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
MOZ_DIAGNOSTIC_ASSERT(aInnerWindow);
|
||||||
|
MOZ_DIAGNOSTIC_ASSERT(aInnerWindow->IsCurrentInnerWindow());
|
||||||
|
MOZ_DIAGNOSTIC_ASSERT(aInnerWindow->HasActiveDocument());
|
||||||
|
|
||||||
|
nsIDocument* doc = aInnerWindow->GetExtantDoc();
|
||||||
|
if (NS_WARN_IF(!doc)) {
|
||||||
|
return NS_ERROR_UNEXPECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't use nsAutoCString here since IPC requires a full nsCString anyway.
|
||||||
|
nsCString spec;
|
||||||
|
|
||||||
|
nsIURI* uri = doc->GetOriginalURI();
|
||||||
|
if (uri) {
|
||||||
|
nsresult rv = uri->GetSpec(spec);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nsPIDOMWindowOuter* outer = aInnerWindow->GetOuterWindow();
|
||||||
|
if (NS_WARN_IF(!outer)) {
|
||||||
|
return NS_ERROR_UNEXPECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameType frameType = FrameType::Top_level;
|
||||||
|
if (!outer->IsTopLevelWindow()) {
|
||||||
|
frameType = FrameType::Nested;
|
||||||
|
} else if(outer->HadOriginalOpener()) {
|
||||||
|
frameType = FrameType::Auxiliary;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We should either be setting a window execution ready for the
|
||||||
|
// first time or setting the same window execution ready again.
|
||||||
|
// The secondary calls are due to initial about:blank replacement.
|
||||||
|
MOZ_DIAGNOSTIC_ASSERT(mOwner.is<Nothing>() ||
|
||||||
|
mOwner.is<nsCOMPtr<nsIDocShell>>() ||
|
||||||
|
GetInnerWindow() == aInnerWindow);
|
||||||
|
|
||||||
|
// This creates a cycle with the window. It is broken when
|
||||||
|
// nsGlobalWindow::FreeInnerObjects() deletes the ClientSource.
|
||||||
|
mOwner = AsVariant(RefPtr<nsPIDOMWindowInner>(aInnerWindow));
|
||||||
|
|
||||||
|
ClientSourceExecutionReadyArgs args(spec, frameType);
|
||||||
|
ExecutionReady(args);
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
ClientSource::DocShellExecutionReady(nsIDocShell* aDocShell)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
MOZ_DIAGNOSTIC_ASSERT(aDocShell);
|
||||||
|
|
||||||
|
nsPIDOMWindowOuter* outer = aDocShell->GetWindow();
|
||||||
|
if (NS_WARN_IF(!outer)) {
|
||||||
|
return NS_ERROR_UNEXPECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: dedupe this with WindowExecutionReady
|
||||||
|
FrameType frameType = FrameType::Top_level;
|
||||||
|
if (!outer->IsTopLevelWindow()) {
|
||||||
|
frameType = FrameType::Nested;
|
||||||
|
} else if(outer->HadOriginalOpener()) {
|
||||||
|
frameType = FrameType::Auxiliary;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_DIAGNOSTIC_ASSERT(mOwner.is<Nothing>());
|
||||||
|
|
||||||
|
// This creates a cycle with the docshell. It is broken when
|
||||||
|
// nsDocShell::Destroy() deletes the ClientSource.
|
||||||
|
mOwner = AsVariant(nsCOMPtr<nsIDocShell>(aDocShell));
|
||||||
|
|
||||||
|
ClientSourceExecutionReadyArgs args(NS_LITERAL_CSTRING("about:blank"),
|
||||||
|
frameType);
|
||||||
|
ExecutionReady(args);
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
|
@ -9,12 +9,16 @@
|
||||||
#include "mozilla/dom/ClientInfo.h"
|
#include "mozilla/dom/ClientInfo.h"
|
||||||
#include "mozilla/dom/ClientThing.h"
|
#include "mozilla/dom/ClientThing.h"
|
||||||
|
|
||||||
|
class nsIDocShell;
|
||||||
|
class nsPIDOMWindowInner;
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace dom {
|
namespace dom {
|
||||||
|
|
||||||
class ClientManager;
|
class ClientManager;
|
||||||
class ClientSourceChild;
|
class ClientSourceChild;
|
||||||
class ClientSourceConstructorArgs;
|
class ClientSourceConstructorArgs;
|
||||||
|
class ClientSourceExecutionReadyArgs;
|
||||||
class PClientManagerChild;
|
class PClientManagerChild;
|
||||||
|
|
||||||
namespace workers {
|
namespace workers {
|
||||||
|
@ -35,11 +39,25 @@ class ClientSource final : public ClientThing<ClientSourceChild>
|
||||||
|
|
||||||
RefPtr<ClientManager> mManager;
|
RefPtr<ClientManager> mManager;
|
||||||
|
|
||||||
|
Variant<Nothing,
|
||||||
|
RefPtr<nsPIDOMWindowInner>,
|
||||||
|
nsCOMPtr<nsIDocShell>,
|
||||||
|
mozilla::dom::workers::WorkerPrivate*> mOwner;
|
||||||
|
|
||||||
ClientInfo mClientInfo;
|
ClientInfo mClientInfo;
|
||||||
|
|
||||||
void
|
void
|
||||||
Shutdown();
|
Shutdown();
|
||||||
|
|
||||||
|
void
|
||||||
|
ExecutionReady(const ClientSourceExecutionReadyArgs& aArgs);
|
||||||
|
|
||||||
|
mozilla::dom::workers::WorkerPrivate*
|
||||||
|
GetWorkerPrivate() const;
|
||||||
|
|
||||||
|
nsIDocShell*
|
||||||
|
GetDocShell() const;
|
||||||
|
|
||||||
// Private methods called by ClientManager
|
// Private methods called by ClientManager
|
||||||
ClientSource(ClientManager* aManager,
|
ClientSource(ClientManager* aManager,
|
||||||
const ClientSourceConstructorArgs& aArgs);
|
const ClientSourceConstructorArgs& aArgs);
|
||||||
|
@ -49,6 +67,18 @@ class ClientSource final : public ClientThing<ClientSourceChild>
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~ClientSource();
|
~ClientSource();
|
||||||
|
|
||||||
|
nsPIDOMWindowInner*
|
||||||
|
GetInnerWindow() const;
|
||||||
|
|
||||||
|
void
|
||||||
|
WorkerExecutionReady(mozilla::dom::workers::WorkerPrivate* aWorkerPrivate);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
WindowExecutionReady(nsPIDOMWindowInner* aInnerWindow);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
DocShellExecutionReady(nsIDocShell* aDocShell);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
|
|
|
@ -9,15 +9,79 @@
|
||||||
#include "ClientHandleParent.h"
|
#include "ClientHandleParent.h"
|
||||||
#include "ClientManagerService.h"
|
#include "ClientManagerService.h"
|
||||||
#include "ClientSourceOpParent.h"
|
#include "ClientSourceOpParent.h"
|
||||||
|
#include "ClientValidation.h"
|
||||||
#include "mozilla/dom/ClientIPCTypes.h"
|
#include "mozilla/dom/ClientIPCTypes.h"
|
||||||
|
#include "mozilla/dom/ContentParent.h"
|
||||||
|
#include "mozilla/dom/PClientManagerParent.h"
|
||||||
|
#include "mozilla/ipc/BackgroundParent.h"
|
||||||
|
#include "mozilla/SystemGroup.h"
|
||||||
#include "mozilla/Unused.h"
|
#include "mozilla/Unused.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace dom {
|
namespace dom {
|
||||||
|
|
||||||
|
using mozilla::ipc::BackgroundParent;
|
||||||
using mozilla::ipc::IPCResult;
|
using mozilla::ipc::IPCResult;
|
||||||
using mozilla::ipc::PrincipalInfo;
|
using mozilla::ipc::PrincipalInfo;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// It would be nice to use a lambda instead of this class, but we cannot
|
||||||
|
// move capture in lambdas yet and ContentParent cannot be AddRef'd off
|
||||||
|
// the main thread.
|
||||||
|
class KillContentParentRunnable final : public Runnable
|
||||||
|
{
|
||||||
|
RefPtr<ContentParent> mContentParent;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit KillContentParentRunnable(RefPtr<ContentParent>&& aContentParent)
|
||||||
|
: Runnable("KillContentParentRunnable")
|
||||||
|
, mContentParent(Move(aContentParent))
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(mContentParent);
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHOD
|
||||||
|
Run()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
mContentParent->KillHard("invalid ClientSourceParent actor");
|
||||||
|
mContentParent = nullptr;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
void
|
||||||
|
ClientSourceParent::KillInvalidChild()
|
||||||
|
{
|
||||||
|
// Try to get the content process before we destroy the actor below.
|
||||||
|
RefPtr<ContentParent> process =
|
||||||
|
BackgroundParent::GetContentParent(Manager()->Manager());
|
||||||
|
|
||||||
|
// First, immediately teardown the ClientSource actor. No matter what
|
||||||
|
// we want to start this process as soon as possible.
|
||||||
|
Unused << ClientSourceParent::Send__delete__(this);
|
||||||
|
|
||||||
|
// If we are running in non-e10s, then there is nothing else to do here.
|
||||||
|
// There is no child process and we don't want to crash the entire browser
|
||||||
|
// in release builds. In general, though, this should not happen in non-e10s
|
||||||
|
// so we do assert this condition.
|
||||||
|
if (!process) {
|
||||||
|
MOZ_DIAGNOSTIC_ASSERT(false, "invalid ClientSourceParent in non-e10s");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In e10s mode we also want to kill the child process. Validation failures
|
||||||
|
// typically mean someone sent us bogus data over the IPC link. We can't
|
||||||
|
// trust that process any more. We have to do this on the main thread, so
|
||||||
|
// there is a small window of time before we kill the process. This is why
|
||||||
|
// we start the actor destruction immediately above.
|
||||||
|
nsCOMPtr<nsIRunnable> r = new KillContentParentRunnable(Move(process));
|
||||||
|
MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget()));
|
||||||
|
}
|
||||||
|
|
||||||
IPCResult
|
IPCResult
|
||||||
ClientSourceParent::RecvTeardown()
|
ClientSourceParent::RecvTeardown()
|
||||||
{
|
{
|
||||||
|
@ -25,10 +89,33 @@ ClientSourceParent::RecvTeardown()
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IPCResult
|
||||||
|
ClientSourceParent::RecvExecutionReady(const ClientSourceExecutionReadyArgs& aArgs)
|
||||||
|
{
|
||||||
|
// Now that we have the creation URL for the Client we can do some validation
|
||||||
|
// to make sure the child actor is not giving us garbage. Since we validate
|
||||||
|
// on the child side as well we treat a failure here as fatal.
|
||||||
|
if (!ClientIsValidCreationURL(mClientInfo.PrincipalInfo(), aArgs.url())) {
|
||||||
|
KillInvalidChild();
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
mClientInfo.SetURL(aArgs.url());
|
||||||
|
mClientInfo.SetFrameType(aArgs.frameType());
|
||||||
|
mExecutionReady = true;
|
||||||
|
|
||||||
|
for (ClientHandleParent* handle : mHandleList) {
|
||||||
|
Unused << handle->SendExecutionReady(mClientInfo.ToIPC());
|
||||||
|
}
|
||||||
|
|
||||||
|
return IPC_OK();
|
||||||
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
ClientSourceParent::ActorDestroy(ActorDestroyReason aReason)
|
ClientSourceParent::ActorDestroy(ActorDestroyReason aReason)
|
||||||
{
|
{
|
||||||
mService->RemoveSource(this);
|
DebugOnly<bool> removed = mService->RemoveSource(this);
|
||||||
|
MOZ_ASSERT(removed);
|
||||||
|
|
||||||
nsTArray<ClientHandleParent*> handleList(mHandleList);
|
nsTArray<ClientHandleParent*> handleList(mHandleList);
|
||||||
for (ClientHandleParent* handle : handleList) {
|
for (ClientHandleParent* handle : handleList) {
|
||||||
|
@ -56,8 +143,8 @@ ClientSourceParent::DeallocPClientSourceOpParent(PClientSourceOpParent* aActor)
|
||||||
ClientSourceParent::ClientSourceParent(const ClientSourceConstructorArgs& aArgs)
|
ClientSourceParent::ClientSourceParent(const ClientSourceConstructorArgs& aArgs)
|
||||||
: mClientInfo(aArgs.id(), aArgs.type(), aArgs.principalInfo(), aArgs.creationTime())
|
: mClientInfo(aArgs.id(), aArgs.type(), aArgs.principalInfo(), aArgs.creationTime())
|
||||||
, mService(ClientManagerService::GetOrCreateInstance())
|
, mService(ClientManagerService::GetOrCreateInstance())
|
||||||
|
, mExecutionReady(false)
|
||||||
{
|
{
|
||||||
mService->AddSource(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientSourceParent::~ClientSourceParent()
|
ClientSourceParent::~ClientSourceParent()
|
||||||
|
@ -65,6 +152,26 @@ ClientSourceParent::~ClientSourceParent()
|
||||||
MOZ_DIAGNOSTIC_ASSERT(mHandleList.IsEmpty());
|
MOZ_DIAGNOSTIC_ASSERT(mHandleList.IsEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ClientSourceParent::Init()
|
||||||
|
{
|
||||||
|
// Ensure the principal is reasonable before adding ourself to the service.
|
||||||
|
// Since we validate the principal on the child side as well, any failure
|
||||||
|
// here is treated as fatal.
|
||||||
|
if (NS_WARN_IF(!ClientIsValidPrincipalInfo(mClientInfo.PrincipalInfo()))) {
|
||||||
|
KillInvalidChild();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Its possible for AddSource() to fail if there is already an entry for
|
||||||
|
// our UUID. This should not normally happen, but could if someone is
|
||||||
|
// spoofing IPC messages.
|
||||||
|
if (NS_WARN_IF(!mService->AddSource(this))) {
|
||||||
|
KillInvalidChild();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const ClientInfo&
|
const ClientInfo&
|
||||||
ClientSourceParent::Info() const
|
ClientSourceParent::Info() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,11 +20,18 @@ class ClientSourceParent final : public PClientSourceParent
|
||||||
ClientInfo mClientInfo;
|
ClientInfo mClientInfo;
|
||||||
RefPtr<ClientManagerService> mService;
|
RefPtr<ClientManagerService> mService;
|
||||||
nsTArray<ClientHandleParent*> mHandleList;
|
nsTArray<ClientHandleParent*> mHandleList;
|
||||||
|
bool mExecutionReady;
|
||||||
|
|
||||||
|
void
|
||||||
|
KillInvalidChild();
|
||||||
|
|
||||||
// PClientSourceParent
|
// PClientSourceParent
|
||||||
IPCResult
|
IPCResult
|
||||||
RecvTeardown() override;
|
RecvTeardown() override;
|
||||||
|
|
||||||
|
IPCResult
|
||||||
|
RecvExecutionReady(const ClientSourceExecutionReadyArgs& aArgs) override;
|
||||||
|
|
||||||
void
|
void
|
||||||
ActorDestroy(ActorDestroyReason aReason) override;
|
ActorDestroy(ActorDestroyReason aReason) override;
|
||||||
|
|
||||||
|
@ -38,6 +45,9 @@ public:
|
||||||
explicit ClientSourceParent(const ClientSourceConstructorArgs& aArgs);
|
explicit ClientSourceParent(const ClientSourceConstructorArgs& aArgs);
|
||||||
~ClientSourceParent();
|
~ClientSourceParent();
|
||||||
|
|
||||||
|
void
|
||||||
|
Init();
|
||||||
|
|
||||||
const ClientInfo&
|
const ClientInfo&
|
||||||
Info() const;
|
Info() const;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,184 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "ClientValidation.h"
|
||||||
|
|
||||||
|
#include "ClientPrefs.h"
|
||||||
|
#include "mozilla/net/MozURL.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
using mozilla::ipc::ContentPrincipalInfo;
|
||||||
|
using mozilla::ipc::PrincipalInfo;
|
||||||
|
using mozilla::net::MozURL;
|
||||||
|
|
||||||
|
bool
|
||||||
|
ClientIsValidPrincipalInfo(const PrincipalInfo& aPrincipalInfo)
|
||||||
|
{
|
||||||
|
// Ideally we would verify that the source process has permission to
|
||||||
|
// create a window or worker with the given principal, but we don't
|
||||||
|
// currently have any such restriction in place. Instead, at least
|
||||||
|
// verify the PrincipalInfo is an expected type and has a parsable
|
||||||
|
// origin/spec.
|
||||||
|
switch (aPrincipalInfo.type()) {
|
||||||
|
// Any system and null principal is acceptable.
|
||||||
|
case PrincipalInfo::TSystemPrincipalInfo:
|
||||||
|
case PrincipalInfo::TNullPrincipalInfo:
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate content principals to ensure that the origin and spec are sane.
|
||||||
|
case PrincipalInfo::TContentPrincipalInfo:
|
||||||
|
{
|
||||||
|
const ContentPrincipalInfo& content =
|
||||||
|
aPrincipalInfo.get_ContentPrincipalInfo();
|
||||||
|
|
||||||
|
// Verify the principal spec parses.
|
||||||
|
RefPtr<MozURL> specURL;
|
||||||
|
nsresult rv = MozURL::Init(getter_AddRefs(specURL), content.spec());
|
||||||
|
NS_ENSURE_SUCCESS(rv, false);
|
||||||
|
|
||||||
|
// Verify the principal originNoSuffix parses.
|
||||||
|
RefPtr<MozURL> originURL;
|
||||||
|
rv = MozURL::Init(getter_AddRefs(originURL),
|
||||||
|
content.originNoSuffix().get_nsCString());
|
||||||
|
NS_ENSURE_SUCCESS(rv, false);
|
||||||
|
|
||||||
|
nsAutoCString originOrigin;
|
||||||
|
rv = originURL->GetOrigin(originOrigin);
|
||||||
|
NS_ENSURE_SUCCESS(rv, false);
|
||||||
|
|
||||||
|
nsAutoCString specOrigin;
|
||||||
|
rv = specURL->GetOrigin(specOrigin);
|
||||||
|
NS_ENSURE_SUCCESS(rv, false);
|
||||||
|
|
||||||
|
// For now require Clients to have a principal where both its
|
||||||
|
// originNoSuffix and spec have the same origin. This will
|
||||||
|
// exclude a variety of unusual combinations within the browser
|
||||||
|
// but its adequate for the features need to support right now.
|
||||||
|
// If necessary we could expand this function to handle more
|
||||||
|
// cases in the future.
|
||||||
|
return specOrigin == originOrigin;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Windows and workers should not have expanded URLs, etc.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ClientIsValidCreationURL(const PrincipalInfo& aPrincipalInfo,
|
||||||
|
const nsACString& aURL)
|
||||||
|
{
|
||||||
|
RefPtr<MozURL> url;
|
||||||
|
nsresult rv = MozURL::Init(getter_AddRefs(url), aURL);
|
||||||
|
NS_ENSURE_SUCCESS(rv, false);
|
||||||
|
|
||||||
|
switch (aPrincipalInfo.type()) {
|
||||||
|
case PrincipalInfo::TContentPrincipalInfo:
|
||||||
|
{
|
||||||
|
// Any origin can create an about:blank or about:srcdoc Client.
|
||||||
|
if (aURL.LowerCaseEqualsLiteral("about:blank") ||
|
||||||
|
aURL.LowerCaseEqualsLiteral("about:srcdoc")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ContentPrincipalInfo& content =
|
||||||
|
aPrincipalInfo.get_ContentPrincipalInfo();
|
||||||
|
|
||||||
|
// Parse the principal origin URL as well. This ensures any MozURL
|
||||||
|
// parser issues effect both URLs equally.
|
||||||
|
RefPtr<MozURL> principalURL;
|
||||||
|
rv = MozURL::Init(getter_AddRefs(principalURL),
|
||||||
|
content.originNoSuffix().get_nsCString());
|
||||||
|
NS_ENSURE_SUCCESS(rv, false);
|
||||||
|
|
||||||
|
nsAutoCString origin;
|
||||||
|
rv = url->GetOrigin(origin);
|
||||||
|
NS_ENSURE_SUCCESS(rv, false);
|
||||||
|
|
||||||
|
nsAutoCString principalOrigin;
|
||||||
|
rv = principalURL->GetOrigin(principalOrigin);
|
||||||
|
NS_ENSURE_SUCCESS(rv, false);
|
||||||
|
|
||||||
|
// The vast majority of sites should simply result in the same principal
|
||||||
|
// and URL origin.
|
||||||
|
if (principalOrigin == origin) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAutoCString scheme;
|
||||||
|
rv = url->GetScheme(scheme);
|
||||||
|
NS_ENSURE_SUCCESS(rv, false);
|
||||||
|
|
||||||
|
// Generally any origin can also open javascript: windows and workers.
|
||||||
|
if (scheme.LowerCaseEqualsLiteral("javascript")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have some tests that use data: URL windows without an opaque
|
||||||
|
// origin. This should only happen when a pref is set.
|
||||||
|
if (!ClientPrefsGetDataURLUniqueOpaqueOrigin() &&
|
||||||
|
scheme.LowerCaseEqualsLiteral("data")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAutoCString principalScheme;
|
||||||
|
rv = principalURL->GetScheme(principalScheme);
|
||||||
|
NS_ENSURE_SUCCESS(rv, false);
|
||||||
|
|
||||||
|
// Otherwise don't support this URL type in the clients sub-system for
|
||||||
|
// now. This will exclude a variety of internal browser clients, but
|
||||||
|
// currently we don't need to support those. This function can be
|
||||||
|
// expanded to handle more cases as necessary.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
case PrincipalInfo::TSystemPrincipalInfo:
|
||||||
|
{
|
||||||
|
nsAutoCString scheme;
|
||||||
|
rv = url->GetScheme(scheme);
|
||||||
|
NS_ENSURE_SUCCESS(rv, false);
|
||||||
|
|
||||||
|
// While many types of documents can be created with a system principal,
|
||||||
|
// there are only a few that can reasonably become windows. We attempt
|
||||||
|
// to validate the list of known cases here with a simple scheme check.
|
||||||
|
return scheme.LowerCaseEqualsLiteral("about") ||
|
||||||
|
scheme.LowerCaseEqualsLiteral("chrome") ||
|
||||||
|
scheme.LowerCaseEqualsLiteral("resource") ||
|
||||||
|
scheme.LowerCaseEqualsLiteral("blob") ||
|
||||||
|
scheme.LowerCaseEqualsLiteral("javascript") ||
|
||||||
|
scheme.LowerCaseEqualsLiteral("view-source") ||
|
||||||
|
|
||||||
|
(!ClientPrefsGetDataURLUniqueOpaqueOrigin() &&
|
||||||
|
scheme.LowerCaseEqualsLiteral("data"));
|
||||||
|
}
|
||||||
|
case PrincipalInfo::TNullPrincipalInfo:
|
||||||
|
{
|
||||||
|
// A wide variety of clients can have a null principal. For example,
|
||||||
|
// sandboxed iframes can have a normal content URL. For now allow
|
||||||
|
// any parsable URL for null principals. This is relatively safe since
|
||||||
|
// null principals have unique origins and won't most ClientManagerService
|
||||||
|
// queries anyway.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clients (windows/workers) should never have an expanded principal type.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
|
@ -0,0 +1,27 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef _mozilla_dom_ClientValidation_h
|
||||||
|
#define _mozilla_dom_ClientValidation_h
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
|
||||||
|
namespace ipc {
|
||||||
|
class PrincipalInfo;
|
||||||
|
} // namespace ipc
|
||||||
|
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
bool
|
||||||
|
ClientIsValidPrincipalInfo(const mozilla::ipc::PrincipalInfo& aPrincipalInfo);
|
||||||
|
|
||||||
|
bool
|
||||||
|
ClientIsValidCreationURL(const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
|
||||||
|
const nsACString& aURL);
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // _mozilla_dom_ClientValidation_h
|
|
@ -25,6 +25,8 @@ parent:
|
||||||
async PClientHandleOp(ClientOpConstructorArgs aArgs);
|
async PClientHandleOp(ClientOpConstructorArgs aArgs);
|
||||||
|
|
||||||
child:
|
child:
|
||||||
|
async ExecutionReady(IPCClientInfo aClientInfo);
|
||||||
|
|
||||||
async __delete__();
|
async __delete__();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ sync protocol PClientSource
|
||||||
|
|
||||||
parent:
|
parent:
|
||||||
async Teardown();
|
async Teardown();
|
||||||
|
async ExecutionReady(ClientSourceExecutionReadyArgs aArgs);
|
||||||
|
|
||||||
child:
|
child:
|
||||||
async PClientSourceOp(ClientOpConstructorArgs aArgs);
|
async PClientSourceOp(ClientOpConstructorArgs aArgs);
|
||||||
|
|
|
@ -36,12 +36,14 @@ UNIFIED_SOURCES += [
|
||||||
'ClientOpenWindowOpActors.cpp',
|
'ClientOpenWindowOpActors.cpp',
|
||||||
'ClientOpenWindowOpChild.cpp',
|
'ClientOpenWindowOpChild.cpp',
|
||||||
'ClientOpenWindowOpParent.cpp',
|
'ClientOpenWindowOpParent.cpp',
|
||||||
|
'ClientPrefs.cpp',
|
||||||
'ClientSource.cpp',
|
'ClientSource.cpp',
|
||||||
'ClientSourceChild.cpp',
|
'ClientSourceChild.cpp',
|
||||||
'ClientSourceOpChild.cpp',
|
'ClientSourceOpChild.cpp',
|
||||||
'ClientSourceOpParent.cpp',
|
'ClientSourceOpParent.cpp',
|
||||||
'ClientSourceParent.cpp',
|
'ClientSourceParent.cpp',
|
||||||
'ClientState.cpp',
|
'ClientState.cpp',
|
||||||
|
'ClientValidation.cpp',
|
||||||
]
|
]
|
||||||
|
|
||||||
IPDL_SOURCES += [
|
IPDL_SOURCES += [
|
||||||
|
|
|
@ -1296,7 +1296,11 @@ ContentParent::Init()
|
||||||
obs->AddObserver(this, sObserverTopics[i], false);
|
obs->AddObserver(this, sObserverTopics[i], false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Register ContentParent as an observer for changes to any pref whose prefix
|
||||||
|
// matches the empty string, i.e. all of them.
|
||||||
Preferences::AddStrongObserver(this, "");
|
Preferences::AddStrongObserver(this, "");
|
||||||
|
|
||||||
if (obs) {
|
if (obs) {
|
||||||
nsAutoString cpId;
|
nsAutoString cpId;
|
||||||
cpId.AppendInt(static_cast<uint64_t>(this->ChildID()));
|
cpId.AppendInt(static_cast<uint64_t>(this->ChildID()));
|
||||||
|
@ -2773,14 +2777,14 @@ ContentParent::Observe(nsISupports* aSubject,
|
||||||
NS_LITERAL_STRING("-no-forward"))) {
|
NS_LITERAL_STRING("-no-forward"))) {
|
||||||
Unused << SendFlushMemory(nsDependentString(aData));
|
Unused << SendFlushMemory(nsDependentString(aData));
|
||||||
}
|
}
|
||||||
// listening for remotePrefs...
|
|
||||||
else if (!strcmp(aTopic, "nsPref:changed")) {
|
else if (!strcmp(aTopic, "nsPref:changed")) {
|
||||||
// Some prefs are not useful in the content process.
|
// A pref changed. If it's not on the blacklist, inform child processes.
|
||||||
#define BLACKLIST_ENTRY(s) { s, (sizeof(s)/sizeof(char16_t)) - 1 }
|
#define BLACKLIST_ENTRY(s) { s, (sizeof(s)/sizeof(char16_t)) - 1 }
|
||||||
struct BlacklistEntry {
|
struct BlacklistEntry {
|
||||||
const char16_t* mPrefBranch;
|
const char16_t* mPrefBranch;
|
||||||
size_t mLen;
|
size_t mLen;
|
||||||
};
|
};
|
||||||
|
// These prefs are not useful in child processes.
|
||||||
static const BlacklistEntry sContentPrefBranchBlacklist[] = {
|
static const BlacklistEntry sContentPrefBranchBlacklist[] = {
|
||||||
BLACKLIST_ENTRY(u"app.update.lastUpdateTime."),
|
BLACKLIST_ENTRY(u"app.update.lastUpdateTime."),
|
||||||
BLACKLIST_ENTRY(u"datareporting.policy."),
|
BLACKLIST_ENTRY(u"datareporting.policy."),
|
||||||
|
|
|
@ -661,11 +661,17 @@ nsEditorSpellCheck::SetCurrentDictionary(const nsAString& aDictionary)
|
||||||
// Also store it in as a preference, so we can use it as a fallback.
|
// Also store it in as a preference, so we can use it as a fallback.
|
||||||
// We don't want this for mail composer because it uses
|
// We don't want this for mail composer because it uses
|
||||||
// "spellchecker.dictionary" as a preference.
|
// "spellchecker.dictionary" as a preference.
|
||||||
Preferences::SetString("spellchecker.dictionary", aDictionary);
|
//
|
||||||
|
// XXX: Prefs can only be set in the parent process, so this condition is
|
||||||
|
// necessary to stop libpref from throwing errors. But this should
|
||||||
|
// probably be handled in a better way.
|
||||||
|
if (XRE_IsParentProcess()) {
|
||||||
|
Preferences::SetString("spellchecker.dictionary", aDictionary);
|
||||||
#ifdef DEBUG_DICT
|
#ifdef DEBUG_DICT
|
||||||
printf("***** Storing spellchecker.dictionary |%s|\n",
|
printf("***** Possibly storing spellchecker.dictionary |%s|\n",
|
||||||
NS_ConvertUTF16toUTF8(aDictionary).get());
|
NS_ConvertUTF16toUTF8(aDictionary).get());
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mSpellChecker->SetCurrentDictionary(aDictionary);
|
return mSpellChecker->SetCurrentDictionary(aDictionary);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
52373
|
52374
|
||||||
0/nm
|
0/nm
|
||||||
0th/pt
|
0th/pt
|
||||||
1/n1
|
1/n1
|
||||||
|
@ -27178,6 +27178,7 @@ fuehrer/MS
|
||||||
fuel's
|
fuel's
|
||||||
fuel/ADGS
|
fuel/ADGS
|
||||||
fug
|
fug
|
||||||
|
fugacious/YP
|
||||||
fugal
|
fugal
|
||||||
fuggy
|
fuggy
|
||||||
fugitive/MS
|
fugitive/MS
|
||||||
|
|
|
@ -389,6 +389,9 @@ ImageBridgeParent::NotifyImageComposites(nsTArray<ImageCompositeNotificationInfo
|
||||||
++end;
|
++end;
|
||||||
}
|
}
|
||||||
RefPtr<ImageBridgeParent> bridge = GetInstance(pid);
|
RefPtr<ImageBridgeParent> bridge = GetInstance(pid);
|
||||||
|
if (!bridge || bridge->mClosed) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
bridge->SendPendingAsyncMessages();
|
bridge->SendPendingAsyncMessages();
|
||||||
if (!bridge->SendDidComposite(notifications)) {
|
if (!bridge->SendDidComposite(notifications)) {
|
||||||
ok = false;
|
ok = false;
|
||||||
|
|
|
@ -137,12 +137,11 @@ AsyncImagePipelineManager::UpdateAsyncImagePipeline(const wr::PipelineId& aPipel
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pipeline->mInitialised = true;
|
pipeline->mInitialised = true;
|
||||||
pipeline->mIsChanged = true;
|
pipeline->Update(aScBounds,
|
||||||
pipeline->mScBounds = aScBounds;
|
aScTransform,
|
||||||
pipeline->mScTransform = aScTransform;
|
aScaleToSize,
|
||||||
pipeline->mScaleToSize = aScaleToSize;
|
aFilter,
|
||||||
pipeline->mFilter = aFilter;
|
aMixBlendMode);
|
||||||
pipeline->mMixBlendMode = aMixBlendMode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Maybe<TextureHost::ResourceUpdateOp>
|
Maybe<TextureHost::ResourceUpdateOp>
|
||||||
|
|
|
@ -120,6 +120,23 @@ private:
|
||||||
|
|
||||||
struct AsyncImagePipeline {
|
struct AsyncImagePipeline {
|
||||||
AsyncImagePipeline();
|
AsyncImagePipeline();
|
||||||
|
void Update(const LayoutDeviceRect& aScBounds,
|
||||||
|
const gfx::Matrix4x4& aScTransform,
|
||||||
|
const gfx::MaybeIntSize& aScaleToSize,
|
||||||
|
const wr::ImageRendering& aFilter,
|
||||||
|
const wr::MixBlendMode& aMixBlendMode)
|
||||||
|
{
|
||||||
|
mIsChanged |= !mScBounds.IsEqualEdges(aScBounds) ||
|
||||||
|
mScTransform != aScTransform ||
|
||||||
|
mScaleToSize != aScaleToSize ||
|
||||||
|
mFilter != aFilter ||
|
||||||
|
mMixBlendMode != aMixBlendMode;
|
||||||
|
mScBounds = aScBounds;
|
||||||
|
mScTransform = aScTransform;
|
||||||
|
mScaleToSize = aScaleToSize;
|
||||||
|
mFilter = aFilter;
|
||||||
|
mMixBlendMode = aMixBlendMode;
|
||||||
|
}
|
||||||
|
|
||||||
bool mInitialised;
|
bool mInitialised;
|
||||||
bool mIsChanged;
|
bool mIsChanged;
|
||||||
|
|
|
@ -670,11 +670,23 @@ WebRenderBridgeParent::RecvEmptyTransaction(const FocusTarget& aFocusTarget,
|
||||||
mScrollData.SetFocusTarget(aFocusTarget);
|
mScrollData.SetFocusTarget(aFocusTarget);
|
||||||
UpdateAPZ(false);
|
UpdateAPZ(false);
|
||||||
|
|
||||||
// XXX Call DidComposite at correct timing.
|
if (!aCommands.IsEmpty()) {
|
||||||
TimeStamp now = TimeStamp::Now();
|
uint32_t wrEpoch = GetNextWrEpoch();
|
||||||
HoldPendingTransactionId(mWrEpoch, aTransactionId, aTxnStartTime, aFwdTime);
|
// Send empty UpdatePipelineResources to WebRender just to notify a new epoch.
|
||||||
mCompositorBridge->DidComposite(wr::AsUint64(mPipelineId), now, now);
|
// The epoch is used to know a timing of calling DidComposite().
|
||||||
|
// This is much simpler than tracking an epoch of AsyncImagePipeline.
|
||||||
|
wr::ResourceUpdateQueue resourceUpdates;
|
||||||
|
mApi->UpdatePipelineResources(resourceUpdates, mPipelineId, wr::NewEpoch(wrEpoch));
|
||||||
|
HoldPendingTransactionId(wrEpoch, aTransactionId, aTxnStartTime, aFwdTime);
|
||||||
|
} else {
|
||||||
|
HoldPendingTransactionId(mWrEpoch, aTransactionId, aTxnStartTime, aFwdTime);
|
||||||
|
// If WebRenderBridgeParent does not have pending DidComposites,
|
||||||
|
// send DidComposite now.
|
||||||
|
if (mPendingTransactionIds.empty()) {
|
||||||
|
TimeStamp now = TimeStamp::Now();
|
||||||
|
mCompositorBridge->DidComposite(wr::AsUint64(mPipelineId), now, now);
|
||||||
|
}
|
||||||
|
}
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "mozilla/Range.h"
|
#include "mozilla/Range.h"
|
||||||
#include "mozilla/gfx/2D.h"
|
#include "mozilla/gfx/2D.h"
|
||||||
#include "mozilla/gfx/InlineTranslator.h"
|
#include "mozilla/gfx/InlineTranslator.h"
|
||||||
|
#include "mozilla/gfx/Logging.h"
|
||||||
#include "mozilla/gfx/RecordedEvent.h"
|
#include "mozilla/gfx/RecordedEvent.h"
|
||||||
#include "WebRenderTypes.h"
|
#include "WebRenderTypes.h"
|
||||||
#include "webrender_ffi.h"
|
#include "webrender_ffi.h"
|
||||||
|
@ -124,10 +125,15 @@ GetUnscaledFont(Translator *aTranslator, wr::FontKey key) {
|
||||||
type,
|
type,
|
||||||
aTranslator->GetFontContext());
|
aTranslator->GetFontContext());
|
||||||
RefPtr<UnscaledFont> unscaledFont;
|
RefPtr<UnscaledFont> unscaledFont;
|
||||||
if (fontResource) {
|
if (!fontResource) {
|
||||||
|
gfxDevCrash(LogReason::NativeFontResourceNotFound) << "Failed to creative NativeFontResource for FontKey " << key.mHandle;
|
||||||
|
} else {
|
||||||
// Instance data is only needed for GDI fonts which webrender does not
|
// Instance data is only needed for GDI fonts which webrender does not
|
||||||
// support.
|
// support.
|
||||||
unscaledFont = fontResource->CreateUnscaledFont(data.mIndex, nullptr, 0);
|
unscaledFont = fontResource->CreateUnscaledFont(data.mIndex, nullptr, 0);
|
||||||
|
if (!unscaledFont) {
|
||||||
|
gfxDevCrash(LogReason::UnscaledFontNotFound) << "Failed to create UnscaledFont for FontKey " << key.mHandle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
data.mUnscaledFont = unscaledFont;
|
data.mUnscaledFont = unscaledFont;
|
||||||
return unscaledFont;
|
return unscaledFont;
|
||||||
|
|
|
@ -61,6 +61,7 @@ SurfaceFormatToImageFormat(gfx::SurfaceFormat aFormat) {
|
||||||
switch (aFormat) {
|
switch (aFormat) {
|
||||||
case gfx::SurfaceFormat::R8G8B8X8:
|
case gfx::SurfaceFormat::R8G8B8X8:
|
||||||
// TODO: use RGBA + opaque flag
|
// TODO: use RGBA + opaque flag
|
||||||
|
case gfx::SurfaceFormat::R8G8B8A8:
|
||||||
return Some(wr::ImageFormat::BGRA8);
|
return Some(wr::ImageFormat::BGRA8);
|
||||||
case gfx::SurfaceFormat::B8G8R8X8:
|
case gfx::SurfaceFormat::B8G8R8X8:
|
||||||
// TODO: WebRender will have a BGRA + opaque flag for this but does not
|
// TODO: WebRender will have a BGRA + opaque flag for this but does not
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "mozilla/mscom/Objref.h"
|
#include "mozilla/mscom/Objref.h"
|
||||||
#include "mozilla/mscom/Registration.h"
|
#include "mozilla/mscom/Registration.h"
|
||||||
#include "mozilla/mscom/Utils.h"
|
#include "mozilla/mscom/Utils.h"
|
||||||
|
#include "mozilla/ThreadLocal.h"
|
||||||
#include "MainThreadUtils.h"
|
#include "MainThreadUtils.h"
|
||||||
#include "mozilla/Assertions.h"
|
#include "mozilla/Assertions.h"
|
||||||
#include "mozilla/DebugOnly.h"
|
#include "mozilla/DebugOnly.h"
|
||||||
|
@ -131,6 +132,111 @@ private:
|
||||||
LiveSet* mLiveSet;
|
LiveSet* mLiveSet;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class MOZ_RAII ReentrySentinel final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ReentrySentinel(Interceptor* aCurrent)
|
||||||
|
: mCurInterceptor(aCurrent)
|
||||||
|
{
|
||||||
|
static const bool kHasTls = tlsSentinelStackTop.init();
|
||||||
|
MOZ_RELEASE_ASSERT(kHasTls);
|
||||||
|
|
||||||
|
mPrevSentinel = tlsSentinelStackTop.get();
|
||||||
|
tlsSentinelStackTop.set(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
~ReentrySentinel()
|
||||||
|
{
|
||||||
|
tlsSentinelStackTop.set(mPrevSentinel);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsOutermost() const
|
||||||
|
{
|
||||||
|
return !(mPrevSentinel && mPrevSentinel->IsMarshaling(mCurInterceptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
ReentrySentinel(const ReentrySentinel&) = delete;
|
||||||
|
ReentrySentinel(ReentrySentinel&&) = delete;
|
||||||
|
ReentrySentinel& operator=(const ReentrySentinel&) = delete;
|
||||||
|
ReentrySentinel& operator=(ReentrySentinel&&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool IsMarshaling(Interceptor* aTopInterceptor) const
|
||||||
|
{
|
||||||
|
return aTopInterceptor == mCurInterceptor ||
|
||||||
|
(mPrevSentinel && mPrevSentinel->IsMarshaling(aTopInterceptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Interceptor* mCurInterceptor;
|
||||||
|
ReentrySentinel* mPrevSentinel;
|
||||||
|
|
||||||
|
static MOZ_THREAD_LOCAL(ReentrySentinel*) tlsSentinelStackTop;
|
||||||
|
};
|
||||||
|
|
||||||
|
MOZ_THREAD_LOCAL(ReentrySentinel*) ReentrySentinel::tlsSentinelStackTop;
|
||||||
|
|
||||||
|
class MOZ_RAII LoggedQIResult final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit LoggedQIResult(REFIID aIid)
|
||||||
|
: mIid(aIid)
|
||||||
|
, mHr(E_UNEXPECTED)
|
||||||
|
, mTarget(nullptr)
|
||||||
|
, mInterceptor(nullptr)
|
||||||
|
, mBegin(TimeStamp::Now())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~LoggedQIResult()
|
||||||
|
{
|
||||||
|
if (!mTarget) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeStamp end(TimeStamp::Now());
|
||||||
|
TimeDuration total(end - mBegin);
|
||||||
|
TimeDuration overhead(total - mNonOverheadDuration);
|
||||||
|
|
||||||
|
InterceptorLog::QI(mHr, mTarget, mIid, mInterceptor, &overhead,
|
||||||
|
&mNonOverheadDuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Log(IUnknown* aTarget, IUnknown* aInterceptor)
|
||||||
|
{
|
||||||
|
mTarget = aTarget;
|
||||||
|
mInterceptor = aInterceptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator=(HRESULT aHr)
|
||||||
|
{
|
||||||
|
mHr = aHr;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator HRESULT()
|
||||||
|
{
|
||||||
|
return mHr;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator TimeDuration*()
|
||||||
|
{
|
||||||
|
return &mNonOverheadDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
LoggedQIResult(const LoggedQIResult&) = delete;
|
||||||
|
LoggedQIResult(LoggedQIResult&&) = delete;
|
||||||
|
LoggedQIResult& operator=(const LoggedQIResult&) = delete;
|
||||||
|
LoggedQIResult& operator=(LoggedQIResult&&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
REFIID mIid;
|
||||||
|
HRESULT mHr;
|
||||||
|
IUnknown* mTarget;
|
||||||
|
IUnknown* mInterceptor;
|
||||||
|
TimeDuration mNonOverheadDuration;
|
||||||
|
TimeStamp mBegin;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
static detail::LiveSet&
|
static detail::LiveSet&
|
||||||
|
@ -240,9 +346,11 @@ Interceptor::GetMarshalSizeMax(REFIID riid, void* pv, DWORD dwDestContext,
|
||||||
void* pvDestContext, DWORD mshlflags,
|
void* pvDestContext, DWORD mshlflags,
|
||||||
DWORD* pSize)
|
DWORD* pSize)
|
||||||
{
|
{
|
||||||
|
detail::ReentrySentinel sentinel(this);
|
||||||
|
|
||||||
HRESULT hr = mStdMarshal->GetMarshalSizeMax(MarshalAs(riid), pv, dwDestContext,
|
HRESULT hr = mStdMarshal->GetMarshalSizeMax(MarshalAs(riid), pv, dwDestContext,
|
||||||
pvDestContext, mshlflags, pSize);
|
pvDestContext, mshlflags, pSize);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr) || !sentinel.IsOutermost()) {
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,6 +372,8 @@ Interceptor::MarshalInterface(IStream* pStm, REFIID riid, void* pv,
|
||||||
DWORD dwDestContext, void* pvDestContext,
|
DWORD dwDestContext, void* pvDestContext,
|
||||||
DWORD mshlflags)
|
DWORD mshlflags)
|
||||||
{
|
{
|
||||||
|
detail::ReentrySentinel sentinel(this);
|
||||||
|
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
|
#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
|
||||||
|
@ -282,7 +392,7 @@ Interceptor::MarshalInterface(IStream* pStm, REFIID riid, void* pv,
|
||||||
|
|
||||||
hr = mStdMarshal->MarshalInterface(pStm, MarshalAs(riid), pv, dwDestContext,
|
hr = mStdMarshal->MarshalInterface(pStm, MarshalAs(riid), pv, dwDestContext,
|
||||||
pvDestContext, mshlflags);
|
pvDestContext, mshlflags);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr) || !sentinel.IsOutermost()) {
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -488,67 +598,6 @@ Interceptor::GetInitialInterceptorForIID(detail::LiveSetAutoLock& aLiveSetLock,
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MOZ_RAII LoggedQIResult final
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit LoggedQIResult(REFIID aIid)
|
|
||||||
: mIid(aIid)
|
|
||||||
, mHr(E_UNEXPECTED)
|
|
||||||
, mTarget(nullptr)
|
|
||||||
, mInterceptor(nullptr)
|
|
||||||
, mBegin(TimeStamp::Now())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~LoggedQIResult()
|
|
||||||
{
|
|
||||||
if (!mTarget) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
TimeStamp end(TimeStamp::Now());
|
|
||||||
TimeDuration total(end - mBegin);
|
|
||||||
TimeDuration overhead(total - mNonOverheadDuration);
|
|
||||||
|
|
||||||
InterceptorLog::QI(mHr, mTarget, mIid, mInterceptor, &overhead,
|
|
||||||
&mNonOverheadDuration);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Log(IUnknown* aTarget, IUnknown* aInterceptor)
|
|
||||||
{
|
|
||||||
mTarget = aTarget;
|
|
||||||
mInterceptor = aInterceptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator=(HRESULT aHr)
|
|
||||||
{
|
|
||||||
mHr = aHr;
|
|
||||||
}
|
|
||||||
|
|
||||||
operator HRESULT()
|
|
||||||
{
|
|
||||||
return mHr;
|
|
||||||
}
|
|
||||||
|
|
||||||
operator TimeDuration*()
|
|
||||||
{
|
|
||||||
return &mNonOverheadDuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
LoggedQIResult(const LoggedQIResult&) = delete;
|
|
||||||
LoggedQIResult(LoggedQIResult&&) = delete;
|
|
||||||
LoggedQIResult& operator=(const LoggedQIResult&) = delete;
|
|
||||||
LoggedQIResult& operator=(LoggedQIResult&&) = delete;
|
|
||||||
|
|
||||||
private:
|
|
||||||
REFIID mIid;
|
|
||||||
HRESULT mHr;
|
|
||||||
IUnknown* mTarget;
|
|
||||||
IUnknown* mInterceptor;
|
|
||||||
TimeDuration mNonOverheadDuration;
|
|
||||||
TimeStamp mBegin;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method contains the core guts of the handling of QueryInterface calls
|
* This method contains the core guts of the handling of QueryInterface calls
|
||||||
* that are delegated to us from the ICallInterceptor.
|
* that are delegated to us from the ICallInterceptor.
|
||||||
|
@ -560,7 +609,7 @@ private:
|
||||||
HRESULT
|
HRESULT
|
||||||
Interceptor::GetInterceptorForIID(REFIID aIid, void** aOutInterceptor)
|
Interceptor::GetInterceptorForIID(REFIID aIid, void** aOutInterceptor)
|
||||||
{
|
{
|
||||||
LoggedQIResult result(aIid);
|
detail::LoggedQIResult result(aIid);
|
||||||
|
|
||||||
if (!aOutInterceptor) {
|
if (!aOutInterceptor) {
|
||||||
return E_INVALIDARG;
|
return E_INVALIDARG;
|
||||||
|
@ -696,19 +745,25 @@ Interceptor::QueryInterfaceTarget(REFIID aIid, void** aOutput,
|
||||||
HRESULT
|
HRESULT
|
||||||
Interceptor::QueryInterface(REFIID riid, void** ppv)
|
Interceptor::QueryInterface(REFIID riid, void** ppv)
|
||||||
{
|
{
|
||||||
|
if (riid == IID_INoMarshal) {
|
||||||
|
// This entire library is designed around marshaling, so there's no point
|
||||||
|
// propagating this QI request all over the place!
|
||||||
|
return E_NOINTERFACE;
|
||||||
|
}
|
||||||
|
|
||||||
return WeakReferenceSupport::QueryInterface(riid, ppv);
|
return WeakReferenceSupport::QueryInterface(riid, ppv);
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT
|
HRESULT
|
||||||
Interceptor::ThreadSafeQueryInterface(REFIID aIid, IUnknown** aOutInterface)
|
Interceptor::ThreadSafeQueryInterface(REFIID aIid, IUnknown** aOutInterface)
|
||||||
{
|
{
|
||||||
if (aIid == IID_INoMarshal) {
|
|
||||||
// This entire library is designed around marshaling, so there's no point
|
|
||||||
// propagating this QI request all over the place!
|
|
||||||
return E_NOINTERFACE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aIid == IID_IStdMarshalInfo) {
|
if (aIid == IID_IStdMarshalInfo) {
|
||||||
|
detail::ReentrySentinel sentinel(this);
|
||||||
|
|
||||||
|
if (!sentinel.IsOutermost()) {
|
||||||
|
return E_NOINTERFACE;
|
||||||
|
}
|
||||||
|
|
||||||
// Do not indicate that this interface is available unless we actually
|
// Do not indicate that this interface is available unless we actually
|
||||||
// support it. We'll check that by looking for a successful call to
|
// support it. We'll check that by looking for a successful call to
|
||||||
// IInterceptorSink::GetHandler()
|
// IInterceptorSink::GetHandler()
|
||||||
|
|
|
@ -18,6 +18,9 @@ namespace jit {
|
||||||
|
|
||||||
OptimizationLevelInfo IonOptimizations;
|
OptimizationLevelInfo IonOptimizations;
|
||||||
|
|
||||||
|
const uint32_t OptimizationInfo::CompilerWarmupThreshold = 1000;
|
||||||
|
const uint32_t OptimizationInfo::CompilerSmallFunctionWarmupThreshold = CompilerWarmupThreshold;
|
||||||
|
|
||||||
void
|
void
|
||||||
OptimizationInfo::initNormalOptimizationInfo()
|
OptimizationInfo::initNormalOptimizationInfo()
|
||||||
{
|
{
|
||||||
|
@ -83,14 +86,12 @@ OptimizationInfo::compilerWarmUpThreshold(JSScript* script, jsbytecode* pc) cons
|
||||||
if (pc == script->code())
|
if (pc == script->code())
|
||||||
pc = nullptr;
|
pc = nullptr;
|
||||||
|
|
||||||
uint32_t warmUpThreshold = compilerWarmUpThreshold_;
|
uint32_t warmUpThreshold = JitOptions.forcedDefaultIonWarmUpThreshold
|
||||||
if (JitOptions.forcedDefaultIonWarmUpThreshold.isSome())
|
.valueOr(compilerWarmUpThreshold_);
|
||||||
warmUpThreshold = JitOptions.forcedDefaultIonWarmUpThreshold.ref();
|
|
||||||
|
|
||||||
if (JitOptions.isSmallFunction(script)) {
|
if (JitOptions.isSmallFunction(script)) {
|
||||||
warmUpThreshold = compilerSmallFunctionWarmUpThreshold_;
|
warmUpThreshold = JitOptions.forcedDefaultIonSmallFunctionWarmUpThreshold
|
||||||
if (JitOptions.forcedDefaultIonSmallFunctionWarmUpThreshold.isSome())
|
.valueOr(compilerSmallFunctionWarmUpThreshold_);
|
||||||
warmUpThreshold = JitOptions.forcedDefaultIonSmallFunctionWarmUpThreshold.ref();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the script is too large to compile on the active thread, we can still
|
// If the script is too large to compile on the active thread, we can still
|
||||||
|
|
|
@ -132,14 +132,14 @@ class OptimizationInfo
|
||||||
uint32_t compilerWarmUpThreshold_;
|
uint32_t compilerWarmUpThreshold_;
|
||||||
|
|
||||||
// Default compiler warmup threshold, unless it is overridden.
|
// Default compiler warmup threshold, unless it is overridden.
|
||||||
static const uint32_t CompilerWarmupThreshold = 1000;
|
static const uint32_t CompilerWarmupThreshold;
|
||||||
|
|
||||||
// How many invocations or loop iterations are needed before small functions
|
// How many invocations or loop iterations are needed before small functions
|
||||||
// are compiled.
|
// are compiled.
|
||||||
uint32_t compilerSmallFunctionWarmUpThreshold_;
|
uint32_t compilerSmallFunctionWarmUpThreshold_;
|
||||||
|
|
||||||
// Default small function compiler warmup threshold, unless it is overridden.
|
// Default small function compiler warmup threshold, unless it is overridden.
|
||||||
static const uint32_t CompilerSmallFunctionWarmupThreshold = CompilerWarmupThreshold;
|
static const uint32_t CompilerSmallFunctionWarmupThreshold;
|
||||||
|
|
||||||
// How many invocations or loop iterations are needed before calls
|
// How many invocations or loop iterations are needed before calls
|
||||||
// are inlined, as a fraction of compilerWarmUpThreshold.
|
// are inlined, as a fraction of compilerWarmUpThreshold.
|
||||||
|
@ -227,9 +227,8 @@ class OptimizationInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
IonRegisterAllocator registerAllocator() const {
|
IonRegisterAllocator registerAllocator() const {
|
||||||
if (JitOptions.forcedRegisterAllocator.isSome())
|
return JitOptions.forcedRegisterAllocator
|
||||||
return JitOptions.forcedRegisterAllocator.ref();
|
.valueOr(registerAllocator_);
|
||||||
return registerAllocator_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool scalarReplacementEnabled() const {
|
bool scalarReplacementEnabled() const {
|
||||||
|
@ -265,9 +264,8 @@ class OptimizationInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t inliningWarmUpThreshold() const {
|
uint32_t inliningWarmUpThreshold() const {
|
||||||
uint32_t compilerWarmUpThreshold = compilerWarmUpThreshold_;
|
uint32_t compilerWarmUpThreshold = JitOptions.forcedDefaultIonWarmUpThreshold
|
||||||
if (JitOptions.forcedDefaultIonWarmUpThreshold.isSome())
|
.valueOr(compilerWarmUpThreshold_);
|
||||||
compilerWarmUpThreshold = JitOptions.forcedDefaultIonWarmUpThreshold.ref();
|
|
||||||
return compilerWarmUpThreshold * inliningWarmUpThresholdFactor_;
|
return compilerWarmUpThreshold * inliningWarmUpThresholdFactor_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7282,9 +7282,8 @@ JS_GetGlobalJitCompilerOption(JSContext* cx, JSJitCompilerOption opt, uint32_t*
|
||||||
*valueOut = jit::JitOptions.baselineWarmUpThreshold;
|
*valueOut = jit::JitOptions.baselineWarmUpThreshold;
|
||||||
break;
|
break;
|
||||||
case JSJITCOMPILER_ION_WARMUP_TRIGGER:
|
case JSJITCOMPILER_ION_WARMUP_TRIGGER:
|
||||||
*valueOut = jit::JitOptions.forcedDefaultIonWarmUpThreshold.isSome()
|
*valueOut = jit::JitOptions.forcedDefaultIonWarmUpThreshold
|
||||||
? jit::JitOptions.forcedDefaultIonWarmUpThreshold.ref()
|
.valueOr(jit::OptimizationInfo::CompilerWarmupThreshold);
|
||||||
: jit::OptimizationInfo::CompilerWarmupThreshold;
|
|
||||||
break;
|
break;
|
||||||
case JSJITCOMPILER_ION_FORCE_IC:
|
case JSJITCOMPILER_ION_FORCE_IC:
|
||||||
*valueOut = jit::JitOptions.forceInlineCaches;
|
*valueOut = jit::JitOptions.forceInlineCaches;
|
||||||
|
|
|
@ -475,6 +475,20 @@ MayHaveAnimationOfProperty(EffectSet* effects, nsCSSPropertyID aProperty)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
MayHaveAnimationOfProperty(const nsIFrame* aFrame, nsCSSPropertyID aProperty)
|
||||||
|
{
|
||||||
|
switch (aProperty) {
|
||||||
|
case eCSSProperty_transform:
|
||||||
|
return aFrame->MayHaveTransformAnimation();
|
||||||
|
case eCSSProperty_opacity:
|
||||||
|
return aFrame->MayHaveOpacityAnimation();
|
||||||
|
default:
|
||||||
|
MOZ_ASSERT_UNREACHABLE("unexpected property");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
nsLayoutUtils::HasAnimationOfProperty(EffectSet* aEffectSet,
|
nsLayoutUtils::HasAnimationOfProperty(EffectSet* aEffectSet,
|
||||||
nsCSSPropertyID aProperty)
|
nsCSSPropertyID aProperty)
|
||||||
|
@ -496,7 +510,18 @@ bool
|
||||||
nsLayoutUtils::HasAnimationOfProperty(const nsIFrame* aFrame,
|
nsLayoutUtils::HasAnimationOfProperty(const nsIFrame* aFrame,
|
||||||
nsCSSPropertyID aProperty)
|
nsCSSPropertyID aProperty)
|
||||||
{
|
{
|
||||||
return HasAnimationOfProperty(EffectSet::GetEffectSet(aFrame), aProperty);
|
if (!MayHaveAnimationOfProperty(aFrame, aProperty)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return HasMatchingAnimations(aFrame,
|
||||||
|
[&aProperty](KeyframeEffectReadOnly& aEffect)
|
||||||
|
{
|
||||||
|
return (aEffect.IsInEffect() || aEffect.IsCurrent()) &&
|
||||||
|
aEffect.HasAnimationOfProperty(aProperty);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
|
@ -669,6 +669,17 @@ nsFrame::Init(nsIContent* aContent,
|
||||||
IncApproximateVisibleCount();
|
IncApproximateVisibleCount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (aPrevInFlow) {
|
||||||
|
mMayHaveOpacityAnimation = aPrevInFlow->MayHaveOpacityAnimation();
|
||||||
|
mMayHaveTransformAnimation = aPrevInFlow->MayHaveTransformAnimation();
|
||||||
|
} else if (mContent) {
|
||||||
|
EffectSet* effectSet = EffectSet::GetEffectSet(this);
|
||||||
|
if (effectSet) {
|
||||||
|
mMayHaveOpacityAnimation = effectSet->MayHaveOpacityAnimation();
|
||||||
|
mMayHaveTransformAnimation = effectSet->MayHaveTransformAnimation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const nsStyleDisplay *disp = StyleDisplay();
|
const nsStyleDisplay *disp = StyleDisplay();
|
||||||
if (disp->HasTransform(this) ||
|
if (disp->HasTransform(this) ||
|
||||||
(IsFrameOfType(eSupportsCSSTransforms) &&
|
(IsFrameOfType(eSupportsCSSTransforms) &&
|
||||||
|
@ -1503,31 +1514,27 @@ nsIFrame::GetMarginRectRelativeToSelf() const
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
nsIFrame::IsTransformed(const nsStyleDisplay* aStyleDisplay,
|
nsIFrame::IsTransformed(const nsStyleDisplay* aStyleDisplay) const
|
||||||
EffectSet* aEffectSet) const
|
|
||||||
{
|
{
|
||||||
return IsCSSTransformed(aStyleDisplay, aEffectSet) ||
|
return IsCSSTransformed(aStyleDisplay) ||
|
||||||
IsSVGTransformed();
|
IsSVGTransformed();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
nsIFrame::IsCSSTransformed(const nsStyleDisplay* aStyleDisplay,
|
nsIFrame::IsCSSTransformed(const nsStyleDisplay* aStyleDisplay) const
|
||||||
EffectSet* aEffectSet) const
|
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aStyleDisplay == StyleDisplay());
|
MOZ_ASSERT(aStyleDisplay == StyleDisplay());
|
||||||
return ((mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
|
return ((mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
|
||||||
(aStyleDisplay->HasTransform(this) ||
|
(aStyleDisplay->HasTransform(this) ||
|
||||||
HasAnimationOfTransform(aEffectSet)));
|
HasAnimationOfTransform()));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
nsIFrame::HasAnimationOfTransform(EffectSet* aEffectSet) const
|
nsIFrame::HasAnimationOfTransform() const
|
||||||
{
|
{
|
||||||
EffectSet* effects =
|
|
||||||
aEffectSet ? aEffectSet : EffectSet::GetEffectSet(this);
|
|
||||||
|
|
||||||
return IsPrimaryFrame() &&
|
return IsPrimaryFrame() &&
|
||||||
nsLayoutUtils::HasAnimationOfProperty(effects, eCSSProperty_transform) &&
|
nsLayoutUtils::HasAnimationOfProperty(this, eCSSProperty_transform) &&
|
||||||
IsFrameOfType(eSupportsCSSTransforms);
|
IsFrameOfType(eSupportsCSSTransforms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1585,33 +1592,32 @@ nsIFrame::Extend3DContext(const nsStyleDisplay* aStyleDisplay, mozilla::EffectSe
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
nsIFrame::Combines3DTransformWithAncestors(const nsStyleDisplay* aStyleDisplay,
|
nsIFrame::Combines3DTransformWithAncestors(const nsStyleDisplay* aStyleDisplay) const
|
||||||
EffectSet* aEffectSet) const
|
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aStyleDisplay == StyleDisplay());
|
MOZ_ASSERT(aStyleDisplay == StyleDisplay());
|
||||||
nsIFrame* parent = GetFlattenedTreeParentPrimaryFrame();
|
nsIFrame* parent = GetFlattenedTreeParentPrimaryFrame();
|
||||||
if (!parent || !parent->Extend3DContext()) {
|
if (!parent || !parent->Extend3DContext()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return IsCSSTransformed(aStyleDisplay, aEffectSet) ||
|
return IsCSSTransformed(aStyleDisplay) ||
|
||||||
BackfaceIsHidden(aStyleDisplay);
|
BackfaceIsHidden(aStyleDisplay);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
nsIFrame::In3DContextAndBackfaceIsHidden(EffectSet* aEffectSet) const
|
nsIFrame::In3DContextAndBackfaceIsHidden() const
|
||||||
{
|
{
|
||||||
// While both tests fail most of the time, test BackfaceIsHidden()
|
// While both tests fail most of the time, test BackfaceIsHidden()
|
||||||
// first since it's likely to fail faster.
|
// first since it's likely to fail faster.
|
||||||
const nsStyleDisplay* disp = StyleDisplay();
|
const nsStyleDisplay* disp = StyleDisplay();
|
||||||
return BackfaceIsHidden(disp) &&
|
return BackfaceIsHidden(disp) &&
|
||||||
Combines3DTransformWithAncestors(disp, aEffectSet);
|
Combines3DTransformWithAncestors(disp);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
nsIFrame::HasPerspective(const nsStyleDisplay* aStyleDisplay, EffectSet* aEffectSet) const
|
nsIFrame::HasPerspective(const nsStyleDisplay* aStyleDisplay) const
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aStyleDisplay == StyleDisplay());
|
MOZ_ASSERT(aStyleDisplay == StyleDisplay());
|
||||||
if (!IsTransformed(aStyleDisplay, aEffectSet)) {
|
if (!IsTransformed(aStyleDisplay)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
nsIFrame* containingBlock = GetContainingBlock(SKIP_SCROLLED_FRAME, aStyleDisplay);
|
nsIFrame* containingBlock = GetContainingBlock(SKIP_SCROLLED_FRAME, aStyleDisplay);
|
||||||
|
@ -2751,8 +2757,8 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
|
||||||
nsRect dirtyRect = aBuilder->GetDirtyRect();
|
nsRect dirtyRect = aBuilder->GetDirtyRect();
|
||||||
|
|
||||||
bool inTransform = aBuilder->IsInTransform();
|
bool inTransform = aBuilder->IsInTransform();
|
||||||
bool isTransformed = IsTransformed(disp, effectSet);
|
bool isTransformed = IsTransformed(disp);
|
||||||
bool hasPerspective = HasPerspective(effectSet);
|
bool hasPerspective = HasPerspective(disp);
|
||||||
// reset blend mode so we can keep track if this stacking context needs have
|
// reset blend mode so we can keep track if this stacking context needs have
|
||||||
// a nsDisplayBlendContainer. Set the blend mode back when the routine exits
|
// a nsDisplayBlendContainer. Set the blend mode back when the routine exits
|
||||||
// so we keep track if the parent stacking context needs a container too.
|
// so we keep track if the parent stacking context needs a container too.
|
||||||
|
@ -9361,7 +9367,7 @@ nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas,
|
||||||
|
|
||||||
const nsStyleDisplay* disp = StyleDisplayWithOptionalParam(aStyleDisplay);
|
const nsStyleDisplay* disp = StyleDisplayWithOptionalParam(aStyleDisplay);
|
||||||
EffectSet* effectSet = EffectSet::GetEffectSet(this);
|
EffectSet* effectSet = EffectSet::GetEffectSet(this);
|
||||||
bool hasTransform = IsTransformed(disp, effectSet);
|
bool hasTransform = IsTransformed(disp);
|
||||||
|
|
||||||
nsRect bounds(nsPoint(0, 0), aNewSize);
|
nsRect bounds(nsPoint(0, 0), aNewSize);
|
||||||
// Store the passed in overflow area if we are a preserve-3d frame or we have
|
// Store the passed in overflow area if we are a preserve-3d frame or we have
|
||||||
|
@ -9479,7 +9485,7 @@ nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas,
|
||||||
/* If we're transformed, transform the overflow rect by the current transformation. */
|
/* If we're transformed, transform the overflow rect by the current transformation. */
|
||||||
if (ChildrenHavePerspective(disp) && sizeChanged) {
|
if (ChildrenHavePerspective(disp) && sizeChanged) {
|
||||||
nsRect newBounds(nsPoint(0, 0), aNewSize);
|
nsRect newBounds(nsPoint(0, 0), aNewSize);
|
||||||
RecomputePerspectiveChildrenOverflow(this, effectSet);
|
RecomputePerspectiveChildrenOverflow(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasTransform) {
|
if (hasTransform) {
|
||||||
|
@ -9529,8 +9535,7 @@ nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas,
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsIFrame::RecomputePerspectiveChildrenOverflow(const nsIFrame* aStartFrame,
|
nsIFrame::RecomputePerspectiveChildrenOverflow(const nsIFrame* aStartFrame)
|
||||||
EffectSet* aEffectSet)
|
|
||||||
{
|
{
|
||||||
nsIFrame::ChildListIterator lists(this);
|
nsIFrame::ChildListIterator lists(this);
|
||||||
for (; !lists.IsDone(); lists.Next()) {
|
for (; !lists.IsDone(); lists.Next()) {
|
||||||
|
@ -9540,7 +9545,7 @@ nsIFrame::RecomputePerspectiveChildrenOverflow(const nsIFrame* aStartFrame,
|
||||||
if (!child->FrameMaintainsOverflow()) {
|
if (!child->FrameMaintainsOverflow()) {
|
||||||
continue; // frame does not maintain overflow rects
|
continue; // frame does not maintain overflow rects
|
||||||
}
|
}
|
||||||
if (child->HasPerspective(aEffectSet)) {
|
if (child->HasPerspective()) {
|
||||||
nsOverflowAreas* overflow =
|
nsOverflowAreas* overflow =
|
||||||
child->GetProperty(nsIFrame::InitialOverflowProperty());
|
child->GetProperty(nsIFrame::InitialOverflowProperty());
|
||||||
nsRect bounds(nsPoint(0, 0), child->GetSize());
|
nsRect bounds(nsPoint(0, 0), child->GetSize());
|
||||||
|
@ -9558,7 +9563,7 @@ nsIFrame::RecomputePerspectiveChildrenOverflow(const nsIFrame* aStartFrame,
|
||||||
// style context. We must find any descendant frames using our size
|
// style context. We must find any descendant frames using our size
|
||||||
// (by recursing into frames that have the same containing block)
|
// (by recursing into frames that have the same containing block)
|
||||||
// to update their overflow rects too.
|
// to update their overflow rects too.
|
||||||
child->RecomputePerspectiveChildrenOverflow(aStartFrame, aEffectSet);
|
child->RecomputePerspectiveChildrenOverflow(aStartFrame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -633,6 +633,8 @@ public:
|
||||||
, mMayHaveWillChangeBudget(false)
|
, mMayHaveWillChangeBudget(false)
|
||||||
, mBuiltBlendContainer(false)
|
, mBuiltBlendContainer(false)
|
||||||
, mIsPrimaryFrame(false)
|
, mIsPrimaryFrame(false)
|
||||||
|
, mMayHaveTransformAnimation(false)
|
||||||
|
, mMayHaveOpacityAnimation(false)
|
||||||
{
|
{
|
||||||
mozilla::PodZero(&mOverflow);
|
mozilla::PodZero(&mOverflow);
|
||||||
}
|
}
|
||||||
|
@ -1763,29 +1765,23 @@ public:
|
||||||
*
|
*
|
||||||
* @param aStyleDisplay: If the caller has this->StyleDisplay(), providing
|
* @param aStyleDisplay: If the caller has this->StyleDisplay(), providing
|
||||||
* it here will improve performance.
|
* it here will improve performance.
|
||||||
* @param aEffectSet: This function may need to look up EffectSet property.
|
|
||||||
* If a caller already have one, pass it in can save property look up
|
|
||||||
* time; otherwise, just left it as nullptr.
|
|
||||||
*/
|
*/
|
||||||
bool IsTransformed(const nsStyleDisplay* aStyleDisplay, mozilla::EffectSet* aEffectSet = nullptr) const;
|
bool IsTransformed(const nsStyleDisplay* aStyleDisplay) const;
|
||||||
bool IsTransformed(mozilla::EffectSet* aEffectSet = nullptr) const {
|
bool IsTransformed() const {
|
||||||
return IsTransformed(StyleDisplay(), aEffectSet);
|
return IsTransformed(StyleDisplay());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as IsTransformed, except that it doesn't take SVG transforms
|
* Same as IsTransformed, except that it doesn't take SVG transforms
|
||||||
* into account.
|
* into account.
|
||||||
*/
|
*/
|
||||||
bool IsCSSTransformed(const nsStyleDisplay* aStyleDisplay, mozilla::EffectSet* aEffectSet = nullptr) const;
|
bool IsCSSTransformed(const nsStyleDisplay* aStyleDisplay) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if this frame has any animation of transform in effect.
|
* True if this frame has any animation of transform in effect.
|
||||||
*
|
*
|
||||||
* @param aEffectSet: This function may need to look up EffectSet property.
|
|
||||||
* If a caller already have one, pass it in can save property look up
|
|
||||||
* time; otherwise, just left it as nullptr.
|
|
||||||
*/
|
*/
|
||||||
bool HasAnimationOfTransform(mozilla::EffectSet* aEffectSet = nullptr) const;
|
bool HasAnimationOfTransform() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the frame is translucent or the frame has opacity
|
* Returns true if the frame is translucent or the frame has opacity
|
||||||
|
@ -1856,14 +1852,10 @@ public:
|
||||||
*
|
*
|
||||||
* @param aStyleDisplay: If the caller has this->StyleDisplay(), providing
|
* @param aStyleDisplay: If the caller has this->StyleDisplay(), providing
|
||||||
* it here will improve performance.
|
* it here will improve performance.
|
||||||
* @param aEffectSet: This function may need to look up EffectSet property.
|
|
||||||
* If a caller already have one, pass it in can save property look up
|
|
||||||
* time; otherwise, just left it as nullptr.
|
|
||||||
*/
|
*/
|
||||||
bool Combines3DTransformWithAncestors(const nsStyleDisplay* aStyleDisplay,
|
bool Combines3DTransformWithAncestors(const nsStyleDisplay* aStyleDisplay) const;
|
||||||
mozilla::EffectSet* aEffectSet = nullptr) const;
|
bool Combines3DTransformWithAncestors() const {
|
||||||
bool Combines3DTransformWithAncestors(mozilla::EffectSet* aEffectSet = nullptr) const {
|
return Combines3DTransformWithAncestors(StyleDisplay());
|
||||||
return Combines3DTransformWithAncestors(StyleDisplay(), aEffectSet);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1871,11 +1863,8 @@ public:
|
||||||
* Extend3DContext(). This is useful because in some cases the hidden
|
* Extend3DContext(). This is useful because in some cases the hidden
|
||||||
* backface can safely be ignored if it could not be visible anyway.
|
* backface can safely be ignored if it could not be visible anyway.
|
||||||
*
|
*
|
||||||
* @param aEffectSet: This function may need to look up EffectSet property.
|
|
||||||
* If a caller already have one, pass it in can save property look up
|
|
||||||
* time; otherwise, just left it as nullptr.
|
|
||||||
*/
|
*/
|
||||||
bool In3DContextAndBackfaceIsHidden(mozilla::EffectSet* aEffectSet = nullptr) const;
|
bool In3DContextAndBackfaceIsHidden() const;
|
||||||
|
|
||||||
bool IsPreserve3DLeaf(const nsStyleDisplay* aStyleDisplay,
|
bool IsPreserve3DLeaf(const nsStyleDisplay* aStyleDisplay,
|
||||||
mozilla::EffectSet* aEffectSet = nullptr) const {
|
mozilla::EffectSet* aEffectSet = nullptr) const {
|
||||||
|
@ -1886,10 +1875,9 @@ public:
|
||||||
return IsPreserve3DLeaf(StyleDisplay(), aEffectSet);
|
return IsPreserve3DLeaf(StyleDisplay(), aEffectSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HasPerspective(const nsStyleDisplay* aStyleDisplay,
|
bool HasPerspective(const nsStyleDisplay* aStyleDisplay) const;
|
||||||
mozilla::EffectSet* aEffectSet = nullptr) const;
|
bool HasPerspective() const {
|
||||||
bool HasPerspective(mozilla::EffectSet* aEffectSet = nullptr) const {
|
return HasPerspective(StyleDisplay());
|
||||||
return HasPerspective(StyleDisplay(), aEffectSet);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChildrenHavePerspective(const nsStyleDisplay* aStyleDisplay) const {
|
bool ChildrenHavePerspective(const nsStyleDisplay* aStyleDisplay) const {
|
||||||
|
@ -1906,8 +1894,7 @@ public:
|
||||||
*/
|
*/
|
||||||
void ComputePreserve3DChildrenOverflow(nsOverflowAreas& aOverflowAreas);
|
void ComputePreserve3DChildrenOverflow(nsOverflowAreas& aOverflowAreas);
|
||||||
|
|
||||||
void RecomputePerspectiveChildrenOverflow(const nsIFrame* aStartFrame,
|
void RecomputePerspectiveChildrenOverflow(const nsIFrame* aStartFrame);
|
||||||
mozilla::EffectSet* aEffectSet = nullptr);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of ancestors between this and the root of our frame tree
|
* Returns the number of ancestors between this and the root of our frame tree
|
||||||
|
@ -4098,6 +4085,19 @@ public:
|
||||||
mIsWrapperBoxNeedingRestyle = aNeedsRestyle;
|
mIsWrapperBoxNeedingRestyle = aNeedsRestyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MayHaveTransformAnimation() const {
|
||||||
|
return mMayHaveTransformAnimation;
|
||||||
|
}
|
||||||
|
void SetMayHaveTransformAnimation() {
|
||||||
|
mMayHaveTransformAnimation = true;
|
||||||
|
}
|
||||||
|
bool MayHaveOpacityAnimation() const {
|
||||||
|
return mMayHaveOpacityAnimation;
|
||||||
|
}
|
||||||
|
void SetMayHaveOpacityAnimation() {
|
||||||
|
mMayHaveOpacityAnimation = true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If this returns true, the frame it's called on should get the
|
* If this returns true, the frame it's called on should get the
|
||||||
* NS_FRAME_HAS_DIRTY_CHILDREN bit set on it by the caller; either directly
|
* NS_FRAME_HAS_DIRTY_CHILDREN bit set on it by the caller; either directly
|
||||||
|
@ -4345,9 +4345,12 @@ private:
|
||||||
*/
|
*/
|
||||||
bool mIsPrimaryFrame : 1;
|
bool mIsPrimaryFrame : 1;
|
||||||
|
|
||||||
|
bool mMayHaveTransformAnimation : 1;
|
||||||
|
bool mMayHaveOpacityAnimation : 1;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
// There is a 3-bit gap left here.
|
// There is a 1-bit gap left here.
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -693,6 +693,7 @@ impl Server {
|
||||||
|
|
||||||
match poll.poll(&mut events, None) {
|
match poll.poll(&mut events, None) {
|
||||||
Ok(_) => {},
|
Ok(_) => {},
|
||||||
|
Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => return Ok(()),
|
||||||
Err(e) => warn!("server poll error: {}", e),
|
Err(e) => warn!("server poll error: {}", e),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "base/basictypes.h"
|
#include "base/basictypes.h"
|
||||||
#include "GeckoProfiler.h"
|
#include "GeckoProfiler.h"
|
||||||
|
@ -34,6 +32,7 @@
|
||||||
#include "mozilla/UniquePtrExtensions.h"
|
#include "mozilla/UniquePtrExtensions.h"
|
||||||
#include "mozilla/URLPreloader.h"
|
#include "mozilla/URLPreloader.h"
|
||||||
#include "mozilla/Variant.h"
|
#include "mozilla/Variant.h"
|
||||||
|
#include "mozilla/Vector.h"
|
||||||
#include "nsAppDirectoryServiceDefs.h"
|
#include "nsAppDirectoryServiceDefs.h"
|
||||||
#include "nsAutoPtr.h"
|
#include "nsAutoPtr.h"
|
||||||
#include "nsCategoryManagerUtils.h"
|
#include "nsCategoryManagerUtils.h"
|
||||||
|
@ -105,18 +104,6 @@ using namespace mozilla;
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define ENSURE_MAIN_PROCESS_WITH_WARNING(func, pref) \
|
|
||||||
do { \
|
|
||||||
if (MOZ_UNLIKELY(!XRE_IsParentProcess())) { \
|
|
||||||
nsPrintfCString msg( \
|
|
||||||
"ENSURE_MAIN_PROCESS: called %s on %s in a non-main process", \
|
|
||||||
func, \
|
|
||||||
pref); \
|
|
||||||
NS_WARNING(msg.get()); \
|
|
||||||
return NS_ERROR_NOT_AVAILABLE; \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#else // DEBUG
|
#else // DEBUG
|
||||||
|
|
||||||
#define ENSURE_MAIN_PROCESS(func, pref) \
|
#define ENSURE_MAIN_PROCESS(func, pref) \
|
||||||
|
@ -124,11 +111,6 @@ using namespace mozilla;
|
||||||
return NS_ERROR_NOT_AVAILABLE; \
|
return NS_ERROR_NOT_AVAILABLE; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ENSURE_MAIN_PROCESS_WITH_WARNING(func, pref) \
|
|
||||||
if (MOZ_UNLIKELY(!XRE_IsParentProcess())) { \
|
|
||||||
return NS_ERROR_NOT_AVAILABLE; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // DEBUG
|
#endif // DEBUG
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
@ -758,12 +740,14 @@ PREF_ClearAllUserPrefs()
|
||||||
return NS_ERROR_NOT_INITIALIZED;
|
return NS_ERROR_NOT_INITIALIZED;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> prefStrings;
|
Vector<const char*> prefNames;
|
||||||
for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
|
for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
|
||||||
auto pref = static_cast<PrefHashEntry*>(iter.Get());
|
auto pref = static_cast<PrefHashEntry*>(iter.Get());
|
||||||
|
|
||||||
if (pref->mPrefFlags.HasUserValue()) {
|
if (pref->mPrefFlags.HasUserValue()) {
|
||||||
prefStrings.push_back(std::string(pref->mKey));
|
if (!prefNames.append(pref->mKey)) {
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
pref->mPrefFlags.SetHasUserValue(false);
|
pref->mPrefFlags.SetHasUserValue(false);
|
||||||
if (!pref->mPrefFlags.HasDefault()) {
|
if (!pref->mPrefFlags.HasDefault()) {
|
||||||
|
@ -772,8 +756,8 @@ PREF_ClearAllUserPrefs()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (std::string& prefString : prefStrings) {
|
for (const char* prefName : prefNames) {
|
||||||
pref_DoCallback(prefString.c_str());
|
pref_DoCallback(prefName);
|
||||||
}
|
}
|
||||||
|
|
||||||
Preferences::HandleDirty();
|
Preferences::HandleDirty();
|
||||||
|
@ -906,14 +890,14 @@ static PrefHashEntry*
|
||||||
pref_HashTableLookup(const char* aKey)
|
pref_HashTableLookup(const char* aKey)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread() || mozilla::ServoStyleSet::IsInServoTraversal());
|
MOZ_ASSERT(NS_IsMainThread() || mozilla::ServoStyleSet::IsInServoTraversal());
|
||||||
MOZ_ASSERT((!XRE_IsContentProcess() || gPhase != START),
|
MOZ_ASSERT((XRE_IsParentProcess() || gPhase != START),
|
||||||
"pref access before commandline prefs set");
|
"pref access before commandline prefs set");
|
||||||
|
|
||||||
// If you're hitting this assertion, you've added a pref access to start up.
|
// If you're hitting this assertion, you've added a pref access to start up.
|
||||||
// Consider moving it later or add it to the whitelist in ContentPrefs.cpp
|
// Consider moving it later or add it to the whitelist in ContentPrefs.cpp
|
||||||
// and get review from a DOM peer
|
// and get review from a DOM peer.
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
if (XRE_IsContentProcess() && gPhase <= END_INIT_PREFS && !gWatchingPref &&
|
if (!XRE_IsParentProcess() && gPhase <= END_INIT_PREFS && !gWatchingPref &&
|
||||||
!InInitArray(aKey)) {
|
!InInitArray(aKey)) {
|
||||||
MOZ_CRASH_UNSAFE_PRINTF(
|
MOZ_CRASH_UNSAFE_PRINTF(
|
||||||
"accessing non-init pref %s before the rest of the prefs are sent", aKey);
|
"accessing non-init pref %s before the rest of the prefs are sent", aKey);
|
||||||
|
@ -2384,7 +2368,7 @@ nsPrefBranch::GetComplexValue(const char* aPrefName,
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
nsAutoCString utf8String;
|
nsAutoCString utf8String;
|
||||||
|
|
||||||
// we have to do this one first because it's different than all the rest
|
// We have to do this one first because it's different to all the rest.
|
||||||
if (aType.Equals(NS_GET_IID(nsIPrefLocalizedString))) {
|
if (aType.Equals(NS_GET_IID(nsIPrefLocalizedString))) {
|
||||||
nsCOMPtr<nsIPrefLocalizedString> theString(
|
nsCOMPtr<nsIPrefLocalizedString> theString(
|
||||||
do_CreateInstance(NS_PREFLOCALIZEDSTRING_CONTRACTID, &rv));
|
do_CreateInstance(NS_PREFLOCALIZEDSTRING_CONTRACTID, &rv));
|
||||||
|
@ -2433,10 +2417,7 @@ nsPrefBranch::GetComplexValue(const char* aPrefName,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aType.Equals(NS_GET_IID(nsIFile))) {
|
if (aType.Equals(NS_GET_IID(nsIFile))) {
|
||||||
if (XRE_IsContentProcess()) {
|
ENSURE_MAIN_PROCESS("GetComplexValue(nsIFile)", aPrefName);
|
||||||
NS_ERROR("cannot get nsIFile pref from content process");
|
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCOMPtr<nsIFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
|
nsCOMPtr<nsIFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
|
||||||
|
|
||||||
|
@ -2451,10 +2432,7 @@ nsPrefBranch::GetComplexValue(const char* aPrefName,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aType.Equals(NS_GET_IID(nsIRelativeFilePref))) {
|
if (aType.Equals(NS_GET_IID(nsIRelativeFilePref))) {
|
||||||
if (XRE_IsContentProcess()) {
|
ENSURE_MAIN_PROCESS("GetComplexValue(nsIRelativeFilePref)", aPrefName);
|
||||||
NS_ERROR("cannot get nsIRelativeFilePref from content process");
|
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsACString::const_iterator keyBegin, strEnd;
|
nsACString::const_iterator keyBegin, strEnd;
|
||||||
utf8String.BeginReading(keyBegin);
|
utf8String.BeginReading(keyBegin);
|
||||||
|
@ -3099,9 +3077,9 @@ void
|
||||||
Preferences::HandleDirty()
|
Preferences::HandleDirty()
|
||||||
{
|
{
|
||||||
if (!XRE_IsParentProcess()) {
|
if (!XRE_IsParentProcess()) {
|
||||||
// TODO: this should really assert because you can't set prefs in a
|
// This path is hit a lot when setting up prefs for content processes. Just
|
||||||
// content process. But so much code currently does this that we just
|
// ignore it in that case, because content processes aren't responsible for
|
||||||
// ignore it for now.
|
// saving prefs.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3166,6 +3144,7 @@ static const char kPrefFileHeader[] =
|
||||||
NS_LINEBREAK;
|
NS_LINEBREAK;
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
// Note: if sShutdown is true, sPreferences will be nullptr.
|
||||||
StaticRefPtr<Preferences> Preferences::sPreferences;
|
StaticRefPtr<Preferences> Preferences::sPreferences;
|
||||||
bool Preferences::sShutdown = false;
|
bool Preferences::sShutdown = false;
|
||||||
|
|
||||||
|
@ -3567,7 +3546,7 @@ Preferences::GetInstanceForService()
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (XRE_IsContentProcess()) {
|
if (!XRE_IsParentProcess()) {
|
||||||
MOZ_ASSERT(gInitPrefs);
|
MOZ_ASSERT(gInitPrefs);
|
||||||
for (unsigned int i = 0; i < gInitPrefs->Length(); i++) {
|
for (unsigned int i = 0; i < gInitPrefs->Length(); i++) {
|
||||||
Preferences::SetPreference(gInitPrefs->ElementAt(i));
|
Preferences::SetPreference(gInitPrefs->ElementAt(i));
|
||||||
|
@ -3636,7 +3615,11 @@ Preferences::InitStaticMembers()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread() || mozilla::ServoStyleSet::IsInServoTraversal());
|
MOZ_ASSERT(NS_IsMainThread() || mozilla::ServoStyleSet::IsInServoTraversal());
|
||||||
|
|
||||||
if (!sShutdown && !sPreferences) {
|
if (MOZ_LIKELY(sPreferences)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sShutdown) {
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
nsCOMPtr<nsIPrefService> prefService =
|
nsCOMPtr<nsIPrefService> prefService =
|
||||||
do_GetService(NS_PREFSERVICE_CONTRACTID);
|
do_GetService(NS_PREFSERVICE_CONTRACTID);
|
||||||
|
@ -3786,10 +3769,7 @@ Preferences::Observe(nsISupports* aSubject,
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
Preferences::ReadUserPrefsFromFile(nsIFile* aFile)
|
Preferences::ReadUserPrefsFromFile(nsIFile* aFile)
|
||||||
{
|
{
|
||||||
if (MOZ_UNLIKELY(!XRE_IsParentProcess())) {
|
ENSURE_MAIN_PROCESS("Preferences::ReadUserPrefsFromFile", "all prefs");
|
||||||
NS_ERROR("must load prefs from parent process");
|
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!aFile) {
|
if (!aFile) {
|
||||||
NS_ERROR("ReadUserPrefsFromFile requires a parameter");
|
NS_ERROR("ReadUserPrefsFromFile requires a parameter");
|
||||||
|
@ -3802,10 +3782,7 @@ Preferences::ReadUserPrefsFromFile(nsIFile* aFile)
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
Preferences::ResetPrefs()
|
Preferences::ResetPrefs()
|
||||||
{
|
{
|
||||||
if (MOZ_UNLIKELY(!XRE_IsParentProcess())) {
|
ENSURE_MAIN_PROCESS("Preferences::ResetPrefs", "all prefs");
|
||||||
NS_ERROR("must reset prefs from parent process");
|
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
NotifyServiceObservers(NS_PREFSERVICE_RESET_TOPIC_ID);
|
NotifyServiceObservers(NS_PREFSERVICE_RESET_TOPIC_ID);
|
||||||
|
|
||||||
|
@ -3818,10 +3795,7 @@ Preferences::ResetPrefs()
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
Preferences::ResetUserPrefs()
|
Preferences::ResetUserPrefs()
|
||||||
{
|
{
|
||||||
if (MOZ_UNLIKELY(!XRE_IsParentProcess())) {
|
ENSURE_MAIN_PROCESS("Preferences::ResetUserPrefs", "all prefs");
|
||||||
NS_ERROR("must reset user prefs from parent process");
|
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
PREF_ClearAllUserPrefs();
|
PREF_ClearAllUserPrefs();
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
@ -4081,10 +4055,7 @@ Preferences::MakeBackupPrefFile(nsIFile* aFile)
|
||||||
nsresult
|
nsresult
|
||||||
Preferences::SavePrefFileInternal(nsIFile* aFile, SaveMethod aSaveMethod)
|
Preferences::SavePrefFileInternal(nsIFile* aFile, SaveMethod aSaveMethod)
|
||||||
{
|
{
|
||||||
if (MOZ_UNLIKELY(!XRE_IsParentProcess())) {
|
ENSURE_MAIN_PROCESS("Preferences::SavePrefFileInternal", "all prefs");
|
||||||
NS_ERROR("must save pref file from parent process");
|
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We allow different behavior here when aFile argument is not null, but it
|
// We allow different behavior here when aFile argument is not null, but it
|
||||||
// happens to be the same as the current file. It is not clear that we
|
// happens to be the same as the current file. It is not clear that we
|
||||||
|
@ -4654,7 +4625,7 @@ Preferences::GetComplex(const char* aPref, const nsIID& aType, void** aResult)
|
||||||
/* static */ nsresult
|
/* static */ nsresult
|
||||||
Preferences::SetCString(const char* aPref, const char* aValue)
|
Preferences::SetCString(const char* aPref, const char* aValue)
|
||||||
{
|
{
|
||||||
ENSURE_MAIN_PROCESS_WITH_WARNING("SetCString", aPref);
|
ENSURE_MAIN_PROCESS("SetCString", aPref);
|
||||||
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
|
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
|
||||||
return PREF_SetCStringPref(aPref, nsDependentCString(aValue), false);
|
return PREF_SetCStringPref(aPref, nsDependentCString(aValue), false);
|
||||||
}
|
}
|
||||||
|
@ -4662,7 +4633,7 @@ Preferences::SetCString(const char* aPref, const char* aValue)
|
||||||
/* static */ nsresult
|
/* static */ nsresult
|
||||||
Preferences::SetCString(const char* aPref, const nsACString& aValue)
|
Preferences::SetCString(const char* aPref, const nsACString& aValue)
|
||||||
{
|
{
|
||||||
ENSURE_MAIN_PROCESS_WITH_WARNING("SetCString", aPref);
|
ENSURE_MAIN_PROCESS("SetCString", aPref);
|
||||||
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
|
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
|
||||||
return PREF_SetCStringPref(aPref, aValue, false);
|
return PREF_SetCStringPref(aPref, aValue, false);
|
||||||
}
|
}
|
||||||
|
@ -4670,7 +4641,7 @@ Preferences::SetCString(const char* aPref, const nsACString& aValue)
|
||||||
/* static */ nsresult
|
/* static */ nsresult
|
||||||
Preferences::SetString(const char* aPref, const char16ptr_t aValue)
|
Preferences::SetString(const char* aPref, const char16ptr_t aValue)
|
||||||
{
|
{
|
||||||
ENSURE_MAIN_PROCESS_WITH_WARNING("SetString", aPref);
|
ENSURE_MAIN_PROCESS("SetString", aPref);
|
||||||
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
|
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
|
||||||
return PREF_SetCStringPref(aPref, NS_ConvertUTF16toUTF8(aValue), false);
|
return PREF_SetCStringPref(aPref, NS_ConvertUTF16toUTF8(aValue), false);
|
||||||
}
|
}
|
||||||
|
@ -4678,7 +4649,7 @@ Preferences::SetString(const char* aPref, const char16ptr_t aValue)
|
||||||
/* static */ nsresult
|
/* static */ nsresult
|
||||||
Preferences::SetString(const char* aPref, const nsAString& aValue)
|
Preferences::SetString(const char* aPref, const nsAString& aValue)
|
||||||
{
|
{
|
||||||
ENSURE_MAIN_PROCESS_WITH_WARNING("SetString", aPref);
|
ENSURE_MAIN_PROCESS("SetString", aPref);
|
||||||
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
|
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
|
||||||
return PREF_SetCStringPref(aPref, NS_ConvertUTF16toUTF8(aValue), false);
|
return PREF_SetCStringPref(aPref, NS_ConvertUTF16toUTF8(aValue), false);
|
||||||
}
|
}
|
||||||
|
@ -4686,7 +4657,7 @@ Preferences::SetString(const char* aPref, const nsAString& aValue)
|
||||||
/* static */ nsresult
|
/* static */ nsresult
|
||||||
Preferences::SetBool(const char* aPref, bool aValue)
|
Preferences::SetBool(const char* aPref, bool aValue)
|
||||||
{
|
{
|
||||||
ENSURE_MAIN_PROCESS_WITH_WARNING("SetBool", aPref);
|
ENSURE_MAIN_PROCESS("SetBool", aPref);
|
||||||
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
|
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
|
||||||
return PREF_SetBoolPref(aPref, aValue, false);
|
return PREF_SetBoolPref(aPref, aValue, false);
|
||||||
}
|
}
|
||||||
|
@ -4694,7 +4665,7 @@ Preferences::SetBool(const char* aPref, bool aValue)
|
||||||
/* static */ nsresult
|
/* static */ nsresult
|
||||||
Preferences::SetInt(const char* aPref, int32_t aValue)
|
Preferences::SetInt(const char* aPref, int32_t aValue)
|
||||||
{
|
{
|
||||||
ENSURE_MAIN_PROCESS_WITH_WARNING("SetInt", aPref);
|
ENSURE_MAIN_PROCESS("SetInt", aPref);
|
||||||
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
|
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
|
||||||
return PREF_SetIntPref(aPref, aValue, false);
|
return PREF_SetIntPref(aPref, aValue, false);
|
||||||
}
|
}
|
||||||
|
@ -4717,7 +4688,7 @@ Preferences::SetComplex(const char* aPref,
|
||||||
/* static */ nsresult
|
/* static */ nsresult
|
||||||
Preferences::ClearUser(const char* aPref)
|
Preferences::ClearUser(const char* aPref)
|
||||||
{
|
{
|
||||||
ENSURE_MAIN_PROCESS_WITH_WARNING("ClearUser", aPref);
|
ENSURE_MAIN_PROCESS("ClearUser", aPref);
|
||||||
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
|
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
|
||||||
return PREF_ClearUserPref(aPref);
|
return PREF_ClearUserPref(aPref);
|
||||||
}
|
}
|
||||||
|
@ -4759,7 +4730,8 @@ Preferences::AddWeakObserver(nsIObserver* aObserver, const char* aPref)
|
||||||
Preferences::RemoveObserver(nsIObserver* aObserver, const char* aPref)
|
Preferences::RemoveObserver(nsIObserver* aObserver, const char* aPref)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aObserver);
|
MOZ_ASSERT(aObserver);
|
||||||
if (!sPreferences && sShutdown) {
|
if (sShutdown) {
|
||||||
|
MOZ_ASSERT(!sPreferences);
|
||||||
return NS_OK; // Observers have been released automatically.
|
return NS_OK; // Observers have been released automatically.
|
||||||
}
|
}
|
||||||
NS_ENSURE_TRUE(sPreferences, NS_ERROR_NOT_AVAILABLE);
|
NS_ENSURE_TRUE(sPreferences, NS_ERROR_NOT_AVAILABLE);
|
||||||
|
@ -4792,7 +4764,8 @@ Preferences::AddWeakObservers(nsIObserver* aObserver, const char** aPrefs)
|
||||||
Preferences::RemoveObservers(nsIObserver* aObserver, const char** aPrefs)
|
Preferences::RemoveObservers(nsIObserver* aObserver, const char** aPrefs)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aObserver);
|
MOZ_ASSERT(aObserver);
|
||||||
if (!sPreferences && sShutdown) {
|
if (sShutdown) {
|
||||||
|
MOZ_ASSERT(!sPreferences);
|
||||||
return NS_OK; // Observers have been released automatically.
|
return NS_OK; // Observers have been released automatically.
|
||||||
}
|
}
|
||||||
NS_ENSURE_TRUE(sPreferences, NS_ERROR_NOT_AVAILABLE);
|
NS_ENSURE_TRUE(sPreferences, NS_ERROR_NOT_AVAILABLE);
|
||||||
|
@ -4855,7 +4828,8 @@ Preferences::UnregisterCallback(PrefChangedFunc aCallback,
|
||||||
MatchKind aMatchKind)
|
MatchKind aMatchKind)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aCallback);
|
MOZ_ASSERT(aCallback);
|
||||||
if (!sPreferences && sShutdown) {
|
if (sShutdown) {
|
||||||
|
MOZ_ASSERT(!sPreferences);
|
||||||
return NS_OK; // Observers have been released automatically.
|
return NS_OK; // Observers have been released automatically.
|
||||||
}
|
}
|
||||||
NS_ENSURE_TRUE(sPreferences, NS_ERROR_NOT_AVAILABLE);
|
NS_ENSURE_TRUE(sPreferences, NS_ERROR_NOT_AVAILABLE);
|
||||||
|
@ -5097,7 +5071,6 @@ Preferences::GetDefaultType(const char* aPref)
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
||||||
#undef ENSURE_MAIN_PROCESS
|
#undef ENSURE_MAIN_PROCESS
|
||||||
#undef ENSURE_MAIN_PROCESS_WITH_WARNING
|
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
// Module and factory stuff
|
// Module and factory stuff
|
||||||
|
|
|
@ -289,13 +289,16 @@ public:
|
||||||
const char* aPref,
|
const char* aPref,
|
||||||
float aDefault = 0.0f);
|
float aDefault = 0.0f);
|
||||||
|
|
||||||
// Used to synchronise preferences between chrome and content processes.
|
// When a content process is created these methods are used to pass prefs in
|
||||||
|
// bulk from the parent process.
|
||||||
static void GetPreferences(InfallibleTArray<PrefSetting>* aPrefs);
|
static void GetPreferences(InfallibleTArray<PrefSetting>* aPrefs);
|
||||||
|
static void SetInitPreferences(nsTArray<PrefSetting>* aPrefs);
|
||||||
|
|
||||||
|
// When a pref is changed in the parent process, these methods are used to
|
||||||
|
// pass the update to content processes.
|
||||||
static void GetPreference(PrefSetting* aPref);
|
static void GetPreference(PrefSetting* aPref);
|
||||||
static void SetPreference(const PrefSetting& aPref);
|
static void SetPreference(const PrefSetting& aPref);
|
||||||
|
|
||||||
static void SetInitPreferences(nsTArray<PrefSetting>* aPrefs);
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
static void SetInitPhase(pref_initPhase phase);
|
static void SetInitPhase(pref_initPhase phase);
|
||||||
static pref_initPhase InitPhase();
|
static pref_initPhase InitPhase();
|
||||||
|
|
|
@ -52,7 +52,7 @@ for var in ('HAVE_STDLIB_H', 'HAVE_UINT8_T', 'HAVE_UINT16_T',
|
||||||
# falling back to the (presumably) slower-on-this-architecture but working
|
# falling back to the (presumably) slower-on-this-architecture but working
|
||||||
# code path. https://bugzilla.mozilla.org/show_bug.cgi?id=822380 has been filed
|
# code path. https://bugzilla.mozilla.org/show_bug.cgi?id=822380 has been filed
|
||||||
# to make the right and more performant fix and push it back upstream.
|
# to make the right and more performant fix and push it back upstream.
|
||||||
if CONFIG['CPU_ARCH'] in ('arm', 'x86', 'x86_64'):
|
if CONFIG['CPU_ARCH'] in ('arm', 'x86', 'x86_64', 'mips', 'mips64'):
|
||||||
DEFINES['CPU_CISC'] = 1
|
DEFINES['CPU_CISC'] = 1
|
||||||
else:
|
else:
|
||||||
# best guess
|
# best guess
|
||||||
|
|
|
@ -0,0 +1,149 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef MinidumpAnalyzerUtils_h
|
||||||
|
#define MinidumpAnalyzerUtils_h
|
||||||
|
|
||||||
|
#ifdef XP_WIN
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace CrashReporter {
|
||||||
|
|
||||||
|
struct MinidumpAnalyzerOptions {
|
||||||
|
bool fullMinidump;
|
||||||
|
std::string forceUseModule;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern MinidumpAnalyzerOptions gMinidumpAnalyzerOptions;
|
||||||
|
|
||||||
|
#ifdef XP_WIN
|
||||||
|
|
||||||
|
#if !defined(_MSC_VER)
|
||||||
|
static inline std::string
|
||||||
|
WideToMBCP(const std::wstring& wide, unsigned int cp, bool* success = nullptr)
|
||||||
|
{
|
||||||
|
char* buffer = nullptr;
|
||||||
|
int buffer_size = WideCharToMultiByte(cp, 0, wide.c_str(),
|
||||||
|
-1, nullptr, 0, nullptr, nullptr);
|
||||||
|
if (buffer_size == 0) {
|
||||||
|
if (success) {
|
||||||
|
*success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = new char[buffer_size];
|
||||||
|
if (buffer == nullptr) {
|
||||||
|
if (success) {
|
||||||
|
*success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
WideCharToMultiByte(cp, 0, wide.c_str(),
|
||||||
|
-1, buffer, buffer_size, nullptr, nullptr);
|
||||||
|
std::string mb = buffer;
|
||||||
|
delete [] buffer;
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
*success = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mb;
|
||||||
|
}
|
||||||
|
#endif /* !defined(_MSC_VER) */
|
||||||
|
|
||||||
|
static inline std::wstring
|
||||||
|
UTF8ToWide(const std::string& aUtf8Str, bool *aSuccess = nullptr)
|
||||||
|
{
|
||||||
|
wchar_t* buffer = nullptr;
|
||||||
|
int buffer_size = MultiByteToWideChar(CP_UTF8, 0, aUtf8Str.c_str(),
|
||||||
|
-1, nullptr, 0);
|
||||||
|
if (buffer_size == 0) {
|
||||||
|
if (aSuccess) {
|
||||||
|
*aSuccess = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return L"";
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = new wchar_t[buffer_size];
|
||||||
|
|
||||||
|
if (buffer == nullptr) {
|
||||||
|
if (aSuccess) {
|
||||||
|
*aSuccess = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return L"";
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiByteToWideChar(CP_UTF8, 0, aUtf8Str.c_str(),
|
||||||
|
-1, buffer, buffer_size);
|
||||||
|
std::wstring str = buffer;
|
||||||
|
delete [] buffer;
|
||||||
|
|
||||||
|
if (aSuccess) {
|
||||||
|
*aSuccess = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline std::string
|
||||||
|
WideToMBCS(const std::wstring &inp) {
|
||||||
|
int buffer_size = WideCharToMultiByte(CP_ACP, 0, inp.c_str(), -1,
|
||||||
|
nullptr, 0, NULL, NULL);
|
||||||
|
if (buffer_size == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<char> buffer(buffer_size);
|
||||||
|
buffer[0] = 0;
|
||||||
|
|
||||||
|
WideCharToMultiByte(CP_ACP, 0, inp.c_str(), -1,
|
||||||
|
buffer.data(), buffer_size, NULL, NULL);
|
||||||
|
|
||||||
|
return buffer.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline std::string
|
||||||
|
UTF8toMBCS(const std::string &inp) {
|
||||||
|
std::wstring wide = UTF8ToWide(inp);
|
||||||
|
std::string ret = WideToMBCS(wide);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // XP_WIN
|
||||||
|
|
||||||
|
// Check if a file exists at the specified path
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
FileExists(const std::string& aPath)
|
||||||
|
{
|
||||||
|
#if defined(XP_WIN)
|
||||||
|
DWORD attrs = GetFileAttributes(UTF8ToWide(aPath).c_str());
|
||||||
|
return (attrs != INVALID_FILE_ATTRIBUTES);
|
||||||
|
#else // Non-Windows
|
||||||
|
struct stat sb;
|
||||||
|
int ret = stat(aPath.c_str(), &sb);
|
||||||
|
if (ret == -1 || !(sb.st_mode & S_IFREG)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
#endif // XP_WIN
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
#endif // MinidumpAnalyzerUtils_h
|
|
@ -0,0 +1,120 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#if XP_WIN && HAVE_64BIT_BUILD
|
||||||
|
|
||||||
|
#include "MozStackFrameSymbolizer.h"
|
||||||
|
|
||||||
|
#include "MinidumpAnalyzerUtils.h"
|
||||||
|
|
||||||
|
#include "processor/cfi_frame_info.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
namespace CrashReporter {
|
||||||
|
|
||||||
|
extern MinidumpAnalyzerOptions gMinidumpAnalyzerOptions;
|
||||||
|
|
||||||
|
using google_breakpad::CFIFrameInfo;
|
||||||
|
|
||||||
|
MozStackFrameSymbolizer::MozStackFrameSymbolizer() :
|
||||||
|
StackFrameSymbolizer(nullptr, nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MozStackFrameSymbolizer::SymbolizerResult
|
||||||
|
MozStackFrameSymbolizer::FillSourceLineInfo(const CodeModules* modules,
|
||||||
|
const SystemInfo* system_info,
|
||||||
|
StackFrame* stack_frame)
|
||||||
|
{
|
||||||
|
SymbolizerResult ret = StackFrameSymbolizer::FillSourceLineInfo(
|
||||||
|
modules, system_info, stack_frame);
|
||||||
|
|
||||||
|
if (ret == kNoError && this->HasImplementation() &&
|
||||||
|
stack_frame->function_name.empty()) {
|
||||||
|
// Breakpad's Stackwalker::InstructionAddressSeemsValid only considers an
|
||||||
|
// address valid if it has associated symbols.
|
||||||
|
//
|
||||||
|
// This makes sense for complete & accurate symbols, but ours may be
|
||||||
|
// incomplete or wrong. Returning a function name tells Breakpad we
|
||||||
|
// recognize this address as code, so it's OK to use in stack scanning.
|
||||||
|
// This function is only called with addresses that land in this module.
|
||||||
|
//
|
||||||
|
// This allows us to fall back to stack scanning in the case where we were
|
||||||
|
// unable to provide CFI.
|
||||||
|
stack_frame->function_name = "<unknown code>";
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
CFIFrameInfo*
|
||||||
|
MozStackFrameSymbolizer::FindCFIFrameInfo(const StackFrame* frame)
|
||||||
|
{
|
||||||
|
std::string modulePath;
|
||||||
|
|
||||||
|
// For unit testing, support loading a specified module instead of
|
||||||
|
// the real one.
|
||||||
|
bool moduleHasBeenReplaced = false;
|
||||||
|
if (gMinidumpAnalyzerOptions.forceUseModule.size() > 0) {
|
||||||
|
modulePath = gMinidumpAnalyzerOptions.forceUseModule;
|
||||||
|
moduleHasBeenReplaced = true;
|
||||||
|
} else {
|
||||||
|
if (!frame->module) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
modulePath = frame->module->code_file();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get/create the unwind parser.
|
||||||
|
auto itMod = mModuleMap.find(modulePath);
|
||||||
|
std::shared_ptr<ModuleUnwindParser> unwindParser;
|
||||||
|
if (itMod != mModuleMap.end()) {
|
||||||
|
unwindParser = itMod->second;
|
||||||
|
} else {
|
||||||
|
unwindParser.reset(new ModuleUnwindParser(modulePath));
|
||||||
|
mModuleMap[modulePath] = unwindParser;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnwindCFI cfi;
|
||||||
|
DWORD offsetAddr;
|
||||||
|
|
||||||
|
if (moduleHasBeenReplaced) {
|
||||||
|
// If we are replacing a module, addresses will never line up.
|
||||||
|
// So just act like the 1st entry is correct.
|
||||||
|
offsetAddr = unwindParser->GetAnyOffsetAddr();
|
||||||
|
} else {
|
||||||
|
offsetAddr = frame->instruction - frame->module->base_address();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!unwindParser->GetCFI(offsetAddr, cfi)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<CFIFrameInfo> rules(new CFIFrameInfo());
|
||||||
|
|
||||||
|
static const size_t exprSize = 50;
|
||||||
|
char expr[exprSize];
|
||||||
|
if (cfi.stackSize == 0) {
|
||||||
|
snprintf(expr, exprSize, "$rsp");
|
||||||
|
} else {
|
||||||
|
snprintf(expr, exprSize, "$rsp %d +", cfi.stackSize);
|
||||||
|
}
|
||||||
|
rules->SetCFARule(expr);
|
||||||
|
|
||||||
|
if (cfi.ripOffset == 0) {
|
||||||
|
snprintf(expr, exprSize, ".cfa ^");
|
||||||
|
} else {
|
||||||
|
snprintf(expr, exprSize, ".cfa %d - ^", cfi.ripOffset);
|
||||||
|
}
|
||||||
|
rules->SetRARule(expr);
|
||||||
|
|
||||||
|
return rules.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace CrashReporter
|
||||||
|
|
||||||
|
#endif // XP_WIN && HAVE_64BIT_BUILD
|
|
@ -0,0 +1,48 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef MozStackFrameSymbolizer_h
|
||||||
|
#define MozStackFrameSymbolizer_h
|
||||||
|
|
||||||
|
#if XP_WIN && HAVE_64BIT_BUILD
|
||||||
|
|
||||||
|
#include "Win64ModuleUnwindMetadata.h"
|
||||||
|
|
||||||
|
#include "google_breakpad/processor/stack_frame_symbolizer.h"
|
||||||
|
#include "google_breakpad/processor/stack_frame.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace CrashReporter {
|
||||||
|
|
||||||
|
using google_breakpad::CodeModule;
|
||||||
|
using google_breakpad::CodeModules;
|
||||||
|
using google_breakpad::SourceLineResolverInterface;
|
||||||
|
using google_breakpad::StackFrame;
|
||||||
|
using google_breakpad::StackFrameSymbolizer;
|
||||||
|
using google_breakpad::SymbolSupplier;
|
||||||
|
using google_breakpad::SystemInfo;
|
||||||
|
|
||||||
|
class MozStackFrameSymbolizer : public StackFrameSymbolizer {
|
||||||
|
using google_breakpad::StackFrameSymbolizer::SymbolizerResult;
|
||||||
|
|
||||||
|
std::map<std::string, std::shared_ptr<ModuleUnwindParser>> mModuleMap;
|
||||||
|
|
||||||
|
public:
|
||||||
|
MozStackFrameSymbolizer();
|
||||||
|
|
||||||
|
virtual SymbolizerResult FillSourceLineInfo(const CodeModules* modules,
|
||||||
|
const SystemInfo* system_info,
|
||||||
|
StackFrame* stack_frame);
|
||||||
|
|
||||||
|
virtual class google_breakpad::CFIFrameInfo* FindCFIFrameInfo(
|
||||||
|
const StackFrame* frame);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace CrashReporter
|
||||||
|
|
||||||
|
#endif // XP_WIN && HAVE_64BIT_BUILD
|
||||||
|
|
||||||
|
#endif // MozStackFrameSymbolizer_h
|
|
@ -0,0 +1,265 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#if XP_WIN && HAVE_64BIT_BUILD
|
||||||
|
|
||||||
|
#include "Win64ModuleUnwindMetadata.h"
|
||||||
|
|
||||||
|
#include "MinidumpAnalyzerUtils.h"
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <winnt.h>
|
||||||
|
#include <ImageHlp.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace CrashReporter {
|
||||||
|
|
||||||
|
union UnwindCode {
|
||||||
|
struct {
|
||||||
|
uint8_t offset_in_prolog;
|
||||||
|
uint8_t unwind_operation_code : 4;
|
||||||
|
uint8_t operation_info : 4;
|
||||||
|
};
|
||||||
|
USHORT frame_offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum UnwindOperationCodes {
|
||||||
|
UWOP_PUSH_NONVOL = 0, // info == register number
|
||||||
|
UWOP_ALLOC_LARGE = 1, // no info, alloc size in next 2 slots
|
||||||
|
UWOP_ALLOC_SMALL = 2, // info == size of allocation / 8 - 1
|
||||||
|
UWOP_SET_FPREG = 3, // no info, FP = RSP + UNWIND_INFO.FPRegOffset*16
|
||||||
|
UWOP_SAVE_NONVOL = 4, // info == register number, offset in next slot
|
||||||
|
UWOP_SAVE_NONVOL_FAR = 5, // info == register number, offset in next 2 slots
|
||||||
|
UWOP_SAVE_XMM = 6, // Version 1; undocumented
|
||||||
|
UWOP_EPILOG = 6, // Version 2; undocumented
|
||||||
|
UWOP_SAVE_XMM_FAR = 7, // Version 1; undocumented
|
||||||
|
UWOP_SPARE = 7, // Version 2; undocumented
|
||||||
|
UWOP_SAVE_XMM128 = 8, // info == XMM reg number, offset in next slot
|
||||||
|
UWOP_SAVE_XMM128_FAR = 9, // info == XMM reg number, offset in next 2 slots
|
||||||
|
UWOP_PUSH_MACHFRAME = 10 // info == 0: no error-code, 1: error-code
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UnwindInfo {
|
||||||
|
uint8_t version : 3;
|
||||||
|
uint8_t flags : 5;
|
||||||
|
uint8_t size_of_prolog;
|
||||||
|
uint8_t count_of_codes;
|
||||||
|
uint8_t frame_register : 4;
|
||||||
|
uint8_t frame_offset : 4;
|
||||||
|
UnwindCode unwind_code[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
ModuleUnwindParser::~ModuleUnwindParser()
|
||||||
|
{
|
||||||
|
if (mImg) {
|
||||||
|
ImageUnload(mImg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void*
|
||||||
|
ModuleUnwindParser::RvaToVa(ULONG aRva)
|
||||||
|
{
|
||||||
|
return ImageRvaToVa(
|
||||||
|
mImg->FileHeader, mImg->MappedAddress, aRva, &mImg->LastRvaSection);
|
||||||
|
}
|
||||||
|
|
||||||
|
ModuleUnwindParser::ModuleUnwindParser(const std::string& aPath)
|
||||||
|
: mPath(aPath)
|
||||||
|
{
|
||||||
|
// Convert wchar to native charset because ImageLoad only takes
|
||||||
|
// a PSTR as input.
|
||||||
|
std::string code_file = UTF8toMBCS(aPath);
|
||||||
|
|
||||||
|
mImg = ImageLoad((PSTR)code_file.c_str(), NULL);
|
||||||
|
if (!mImg || !mImg->FileHeader) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PIMAGE_OPTIONAL_HEADER64 optional_header = &mImg->FileHeader->OptionalHeader;
|
||||||
|
if (optional_header->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD exception_rva = optional_header->
|
||||||
|
DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress;
|
||||||
|
|
||||||
|
DWORD exception_size = optional_header->
|
||||||
|
DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size;
|
||||||
|
|
||||||
|
auto funcs = (PIMAGE_RUNTIME_FUNCTION_ENTRY)RvaToVa(exception_rva);
|
||||||
|
if (!funcs) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (DWORD i = 0; i < exception_size / sizeof(*funcs); i++) {
|
||||||
|
mUnwindMap[funcs[i].BeginAddress] = &funcs[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ModuleUnwindParser::GenerateCFIForFunction(IMAGE_RUNTIME_FUNCTION_ENTRY& aFunc,
|
||||||
|
UnwindCFI& aRet)
|
||||||
|
{
|
||||||
|
DWORD unwind_rva = aFunc.UnwindInfoAddress;
|
||||||
|
// Holds RVA to all visited IMAGE_RUNTIME_FUNCTION_ENTRY, to avoid
|
||||||
|
// circular references.
|
||||||
|
std::set<DWORD> visited;
|
||||||
|
|
||||||
|
// Follow chained function entries
|
||||||
|
while (unwind_rva & 0x1) {
|
||||||
|
unwind_rva ^= 0x1;
|
||||||
|
|
||||||
|
if (visited.end() != visited.find(unwind_rva)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
visited.insert(unwind_rva);
|
||||||
|
|
||||||
|
auto chained_func = (PIMAGE_RUNTIME_FUNCTION_ENTRY)RvaToVa(unwind_rva);
|
||||||
|
if (!chained_func) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
unwind_rva = chained_func->UnwindInfoAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
visited.insert(unwind_rva);
|
||||||
|
|
||||||
|
auto unwind_info = (UnwindInfo*)RvaToVa(unwind_rva);
|
||||||
|
if (!unwind_info) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD stack_size = 8; // minimal stack size is 8 for RIP
|
||||||
|
DWORD rip_offset = 8;
|
||||||
|
do {
|
||||||
|
for (uint8_t c = 0; c < unwind_info->count_of_codes; c++) {
|
||||||
|
UnwindCode* unwind_code = &unwind_info->unwind_code[c];
|
||||||
|
switch (unwind_code->unwind_operation_code) {
|
||||||
|
case UWOP_PUSH_NONVOL: {
|
||||||
|
stack_size += 8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UWOP_ALLOC_LARGE: {
|
||||||
|
if (unwind_code->operation_info == 0) {
|
||||||
|
c++;
|
||||||
|
if (c < unwind_info->count_of_codes) {
|
||||||
|
stack_size += (unwind_code + 1)->frame_offset * 8;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c += 2;
|
||||||
|
if (c < unwind_info->count_of_codes) {
|
||||||
|
stack_size += (unwind_code + 1)->frame_offset |
|
||||||
|
((unwind_code + 2)->frame_offset << 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UWOP_ALLOC_SMALL: {
|
||||||
|
stack_size += unwind_code->operation_info * 8 + 8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UWOP_SET_FPREG:
|
||||||
|
// To correctly track RSP when it's been transferred to another
|
||||||
|
// register, we would need to emit CFI records for every unwind op.
|
||||||
|
// For simplicity, don't emit CFI records for this function as
|
||||||
|
// we know it will be incorrect after this point.
|
||||||
|
return false;
|
||||||
|
case UWOP_SAVE_NONVOL:
|
||||||
|
case UWOP_SAVE_XMM: // also v2 UWOP_EPILOG
|
||||||
|
case UWOP_SAVE_XMM128: {
|
||||||
|
c++; // skip slot with offset
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UWOP_SAVE_NONVOL_FAR:
|
||||||
|
case UWOP_SAVE_XMM_FAR: // also v2 UWOP_SPARE
|
||||||
|
case UWOP_SAVE_XMM128_FAR: {
|
||||||
|
c += 2; // skip 2 slots with offset
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UWOP_PUSH_MACHFRAME: {
|
||||||
|
if (unwind_code->operation_info) {
|
||||||
|
stack_size += 88;
|
||||||
|
} else {
|
||||||
|
stack_size += 80;
|
||||||
|
}
|
||||||
|
rip_offset += 80;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unwind_info->flags & UNW_FLAG_CHAININFO) {
|
||||||
|
auto chained_func = (PIMAGE_RUNTIME_FUNCTION_ENTRY)(
|
||||||
|
(unwind_info->unwind_code +
|
||||||
|
((unwind_info->count_of_codes + 1) & ~1)));
|
||||||
|
|
||||||
|
if (visited.end() != visited.find(chained_func->UnwindInfoAddress)) {
|
||||||
|
return false; // Circular reference
|
||||||
|
}
|
||||||
|
|
||||||
|
visited.insert(chained_func->UnwindInfoAddress);
|
||||||
|
|
||||||
|
unwind_info = (UnwindInfo*)RvaToVa(chained_func->UnwindInfoAddress);
|
||||||
|
} else {
|
||||||
|
unwind_info = nullptr;
|
||||||
|
}
|
||||||
|
} while (unwind_info);
|
||||||
|
|
||||||
|
aRet.beginAddress = aFunc.BeginAddress;
|
||||||
|
aRet.size = aFunc.EndAddress - aFunc.BeginAddress;
|
||||||
|
aRet.stackSize = stack_size;
|
||||||
|
aRet.ripOffset = rip_offset;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For unit testing we sometimes need any address that's valid in this module.
|
||||||
|
// Just return the first address we know of.
|
||||||
|
DWORD
|
||||||
|
ModuleUnwindParser::GetAnyOffsetAddr() const {
|
||||||
|
if (mUnwindMap.size() < 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return mUnwindMap.begin()->first;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ModuleUnwindParser::GetCFI(DWORD aAddress, UnwindCFI& aRet)
|
||||||
|
{
|
||||||
|
// Figure out the begin address of the requested address.
|
||||||
|
auto itUW = mUnwindMap.lower_bound(aAddress + 1);
|
||||||
|
if (itUW == mUnwindMap.begin()) {
|
||||||
|
return false; // address before this module.
|
||||||
|
}
|
||||||
|
--itUW;
|
||||||
|
|
||||||
|
// Ensure that the function entry is big enough to contain this address.
|
||||||
|
IMAGE_RUNTIME_FUNCTION_ENTRY& func = *itUW->second;
|
||||||
|
if (aAddress > func.EndAddress) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do we have CFI for this function already?
|
||||||
|
auto itCFI = mCFIMap.find(aAddress);
|
||||||
|
if (itCFI != mCFIMap.end()) {
|
||||||
|
aRet = itCFI->second;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No, generate it.
|
||||||
|
if (!GenerateCFIForFunction(func, aRet)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mCFIMap[func.BeginAddress] = aRet;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
#endif // XP_WIN && HAVE_64BIT_BUILD
|
|
@ -0,0 +1,57 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef Win64ModuleUnwindMetadata_h
|
||||||
|
#define Win64ModuleUnwindMetadata_h
|
||||||
|
|
||||||
|
#if XP_WIN && HAVE_64BIT_BUILD
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <winnt.h>
|
||||||
|
#include <ImageHlp.h>
|
||||||
|
|
||||||
|
namespace CrashReporter {
|
||||||
|
|
||||||
|
struct UnwindCFI
|
||||||
|
{
|
||||||
|
uint32_t beginAddress;
|
||||||
|
uint32_t size;
|
||||||
|
uint32_t stackSize;
|
||||||
|
uint32_t ripOffset;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Does lazy-parsing of unwind info.
|
||||||
|
class ModuleUnwindParser {
|
||||||
|
PLOADED_IMAGE mImg;
|
||||||
|
std::string mPath;
|
||||||
|
|
||||||
|
// Maps begin address to exception record.
|
||||||
|
// Populated upon construction.
|
||||||
|
std::map<DWORD, PIMAGE_RUNTIME_FUNCTION_ENTRY> mUnwindMap;
|
||||||
|
|
||||||
|
// Maps begin address to CFI.
|
||||||
|
// Populated as needed.
|
||||||
|
std::map<DWORD, UnwindCFI> mCFIMap;
|
||||||
|
|
||||||
|
bool GenerateCFIForFunction(IMAGE_RUNTIME_FUNCTION_ENTRY& aFunc,
|
||||||
|
UnwindCFI& aRet);
|
||||||
|
void* RvaToVa(ULONG aRva);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ModuleUnwindParser(const std::string& aPath);
|
||||||
|
~ModuleUnwindParser();
|
||||||
|
bool GetCFI(DWORD aAddress, UnwindCFI& aRet);
|
||||||
|
DWORD GetAnyOffsetAddr() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
#endif // XP_WIN && HAVE_64BIT_BUILD
|
||||||
|
|
||||||
|
#endif // Win64ModuleUnwindMetadata_h
|
|
@ -32,12 +32,11 @@
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Path of the minidump to be analyzed.
|
#include "MinidumpAnalyzerUtils.h"
|
||||||
static string gMinidumpPath;
|
|
||||||
|
|
||||||
// When set to true print out the full minidump analysis, otherwise only
|
#if XP_WIN && HAVE_64BIT_BUILD
|
||||||
// include the crashing thread in the output.
|
#include "MozStackFrameSymbolizer.h"
|
||||||
static bool gFullMinidump = false;
|
#endif
|
||||||
|
|
||||||
namespace CrashReporter {
|
namespace CrashReporter {
|
||||||
|
|
||||||
|
@ -62,77 +61,10 @@ using google_breakpad::ProcessResult;
|
||||||
using google_breakpad::ProcessState;
|
using google_breakpad::ProcessState;
|
||||||
using google_breakpad::StackFrame;
|
using google_breakpad::StackFrame;
|
||||||
|
|
||||||
#ifdef XP_WIN
|
MinidumpAnalyzerOptions gMinidumpAnalyzerOptions;
|
||||||
|
|
||||||
#if !defined(_MSC_VER)
|
// Path of the minidump to be analyzed.
|
||||||
static string WideToMBCP(const wstring& wide,
|
static string gMinidumpPath;
|
||||||
unsigned int cp,
|
|
||||||
bool* success = nullptr)
|
|
||||||
{
|
|
||||||
char* buffer = nullptr;
|
|
||||||
int buffer_size = WideCharToMultiByte(cp, 0, wide.c_str(),
|
|
||||||
-1, nullptr, 0, nullptr, nullptr);
|
|
||||||
if(buffer_size == 0) {
|
|
||||||
if (success)
|
|
||||||
*success = false;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer = new char[buffer_size];
|
|
||||||
if(buffer == nullptr) {
|
|
||||||
if (success)
|
|
||||||
*success = false;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
WideCharToMultiByte(cp, 0, wide.c_str(),
|
|
||||||
-1, buffer, buffer_size, nullptr, nullptr);
|
|
||||||
string mb = buffer;
|
|
||||||
delete [] buffer;
|
|
||||||
|
|
||||||
if (success)
|
|
||||||
*success = true;
|
|
||||||
|
|
||||||
return mb;
|
|
||||||
}
|
|
||||||
#endif /* !defined(_MSC_VER) */
|
|
||||||
|
|
||||||
static wstring UTF8ToWide(const string& aUtf8Str, bool *aSuccess = nullptr)
|
|
||||||
{
|
|
||||||
wchar_t* buffer = nullptr;
|
|
||||||
int buffer_size = MultiByteToWideChar(CP_UTF8, 0, aUtf8Str.c_str(),
|
|
||||||
-1, nullptr, 0);
|
|
||||||
if (buffer_size == 0) {
|
|
||||||
if (aSuccess) {
|
|
||||||
*aSuccess = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return L"";
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer = new wchar_t[buffer_size];
|
|
||||||
|
|
||||||
if (buffer == nullptr) {
|
|
||||||
if (aSuccess) {
|
|
||||||
*aSuccess = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return L"";
|
|
||||||
}
|
|
||||||
|
|
||||||
MultiByteToWideChar(CP_UTF8, 0, aUtf8Str.c_str(),
|
|
||||||
-1, buffer, buffer_size);
|
|
||||||
wstring str = buffer;
|
|
||||||
delete [] buffer;
|
|
||||||
|
|
||||||
if (aSuccess) {
|
|
||||||
*aSuccess = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct ModuleCompare {
|
struct ModuleCompare {
|
||||||
bool operator() (const CodeModule* aLhs, const CodeModule* aRhs) const {
|
bool operator() (const CodeModule* aLhs, const CodeModule* aRhs) const {
|
||||||
|
@ -317,7 +249,8 @@ ConvertProcessStateToJSON(const ProcessState& aProcessState, Json::Value& aRoot)
|
||||||
// Record the crashing thread index only if this is a full minidump
|
// Record the crashing thread index only if this is a full minidump
|
||||||
// and all threads' stacks are present, otherwise only the crashing
|
// and all threads' stacks are present, otherwise only the crashing
|
||||||
// thread stack is written out and this field is set to 0.
|
// thread stack is written out and this field is set to 0.
|
||||||
crashInfo["crashing_thread"] = gFullMinidump ? requestingThread : 0;
|
crashInfo["crashing_thread"] =
|
||||||
|
gMinidumpAnalyzerOptions.fullMinidump ? requestingThread : 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
crashInfo["type"] = Json::Value(Json::nullValue);
|
crashInfo["type"] = Json::Value(Json::nullValue);
|
||||||
|
@ -345,7 +278,7 @@ ConvertProcessStateToJSON(const ProcessState& aProcessState, Json::Value& aRoot)
|
||||||
Json::Value threads(Json::arrayValue);
|
Json::Value threads(Json::arrayValue);
|
||||||
int threadCount = aProcessState.threads()->size();
|
int threadCount = aProcessState.threads()->size();
|
||||||
|
|
||||||
if (!gFullMinidump && (requestingThread != -1)) {
|
if (!gMinidumpAnalyzerOptions.fullMinidump && (requestingThread != -1)) {
|
||||||
// Only add the crashing thread
|
// Only add the crashing thread
|
||||||
Json::Value thread;
|
Json::Value thread;
|
||||||
Json::Value stack(Json::arrayValue);
|
Json::Value stack(Json::arrayValue);
|
||||||
|
@ -374,9 +307,14 @@ ConvertProcessStateToJSON(const ProcessState& aProcessState, Json::Value& aRoot)
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
ProcessMinidump(Json::Value& aRoot, const string& aDumpFile) {
|
ProcessMinidump(Json::Value& aRoot, const string& aDumpFile) {
|
||||||
|
#if XP_WIN && HAVE_64BIT_BUILD
|
||||||
|
MozStackFrameSymbolizer symbolizer;
|
||||||
|
MinidumpProcessor minidumpProcessor(&symbolizer, false);
|
||||||
|
#else
|
||||||
BasicSourceLineResolver resolver;
|
BasicSourceLineResolver resolver;
|
||||||
// We don't have a valid symbol resolver so we pass nullptr instead.
|
// We don't have a valid symbol resolver so we pass nullptr instead.
|
||||||
MinidumpProcessor minidumpProcessor(nullptr, &resolver);
|
MinidumpProcessor minidumpProcessor(nullptr, &resolver);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Process the minidump.
|
// Process the minidump.
|
||||||
Minidump dump(aDumpFile);
|
Minidump dump(aDumpFile);
|
||||||
|
@ -415,25 +353,6 @@ OpenAppend(const string& aFilename)
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a file exists at the specified path
|
|
||||||
|
|
||||||
static bool
|
|
||||||
FileExists(const string& aPath)
|
|
||||||
{
|
|
||||||
#if defined(XP_WIN)
|
|
||||||
DWORD attrs = GetFileAttributes(UTF8ToWide(aPath).c_str());
|
|
||||||
return (attrs != INVALID_FILE_ATTRIBUTES);
|
|
||||||
#else // Non-Windows
|
|
||||||
struct stat sb;
|
|
||||||
int ret = stat(aPath.c_str(), &sb);
|
|
||||||
if (ret == -1 || !(sb.st_mode & S_IFREG)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
#endif // XP_WIN
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the extra data file by adding the StackTraces field holding the
|
// Update the extra data file by adding the StackTraces field holding the
|
||||||
// JSON output of this program.
|
// JSON output of this program.
|
||||||
|
|
||||||
|
@ -473,7 +392,10 @@ ParseArguments(int argc, char** argv) {
|
||||||
|
|
||||||
for (int i = 1; i < argc - 1; i++) {
|
for (int i = 1; i < argc - 1; i++) {
|
||||||
if (strcmp(argv[i], "--full") == 0) {
|
if (strcmp(argv[i], "--full") == 0) {
|
||||||
gFullMinidump = true;
|
gMinidumpAnalyzerOptions.fullMinidump = true;
|
||||||
|
} else if ((strcmp(argv[i], "--force-use-module") == 0) && (i < argc - 2)) {
|
||||||
|
gMinidumpAnalyzerOptions.forceUseModule = argv[i + 1];
|
||||||
|
++i;
|
||||||
} else {
|
} else {
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,18 @@ if CONFIG['OS_TARGET'] != 'Android':
|
||||||
if CONFIG['OS_TARGET'] == 'Darwin':
|
if CONFIG['OS_TARGET'] == 'Darwin':
|
||||||
DIST_SUBDIR = 'crashreporter.app/Contents/MacOS'
|
DIST_SUBDIR = 'crashreporter.app/Contents/MacOS'
|
||||||
|
|
||||||
|
if CONFIG['OS_TARGET'] == 'WINNT' and CONFIG['CPU_ARCH'] == 'x86_64':
|
||||||
|
UNIFIED_SOURCES += [
|
||||||
|
'MozStackFrameSymbolizer.cpp',
|
||||||
|
'Win64ModuleUnwindMetadata.cpp',
|
||||||
|
]
|
||||||
|
|
||||||
|
OS_LIBS += [
|
||||||
|
'Dbghelp',
|
||||||
|
'Imagehlp'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
# Don't use the STL wrappers in the crashreporter clients; they don't
|
# Don't use the STL wrappers in the crashreporter clients; they don't
|
||||||
# link with -lmozalloc, and it really doesn't matter here anyway.
|
# link with -lmozalloc, and it really doesn't matter here anyway.
|
||||||
DisableStlWrapping()
|
DisableStlWrapping()
|
||||||
|
|
|
@ -8,6 +8,7 @@ this.CrashTestUtils = {
|
||||||
crash: null,
|
crash: null,
|
||||||
dumpHasStream: null,
|
dumpHasStream: null,
|
||||||
dumpHasInstructionPointerMemory: null,
|
dumpHasInstructionPointerMemory: null,
|
||||||
|
dumpWin64CFITestSymbols: null,
|
||||||
|
|
||||||
// Constants for crash()
|
// Constants for crash()
|
||||||
// Keep these in sync with nsTestCrasher.cpp!
|
// Keep these in sync with nsTestCrasher.cpp!
|
||||||
|
@ -18,6 +19,18 @@ this.CrashTestUtils = {
|
||||||
CRASH_MOZ_CRASH: 4,
|
CRASH_MOZ_CRASH: 4,
|
||||||
CRASH_ABORT: 5,
|
CRASH_ABORT: 5,
|
||||||
CRASH_UNCAUGHT_EXCEPTION: 6,
|
CRASH_UNCAUGHT_EXCEPTION: 6,
|
||||||
|
CRASH_X64CFI_NO_MANS_LAND: 7,
|
||||||
|
CRASH_X64CFI_LAUNCHER: 8,
|
||||||
|
CRASH_X64CFI_UNKNOWN_OPCODE: 9,
|
||||||
|
CRASH_X64CFI_PUSH_NONVOL: 10,
|
||||||
|
CRASH_X64CFI_ALLOC_SMALL: 11,
|
||||||
|
CRASH_X64CFI_ALLOC_LARGE: 12,
|
||||||
|
CRASH_X64CFI_SAVE_NONVOL: 15,
|
||||||
|
CRASH_X64CFI_SAVE_NONVOL_FAR: 16,
|
||||||
|
CRASH_X64CFI_SAVE_XMM128: 17,
|
||||||
|
CRASH_X64CFI_SAVE_XMM128_FAR: 18,
|
||||||
|
CRASH_X64CFI_EPILOG: 19,
|
||||||
|
CRASH_X64CFI_EOF: 20,
|
||||||
|
|
||||||
// Constants for dumpHasStream()
|
// Constants for dumpHasStream()
|
||||||
// From google_breakpad/common/minidump_format.h
|
// From google_breakpad/common/minidump_format.h
|
||||||
|
@ -63,3 +76,9 @@ CrashTestUtils.dumpCheckMemory = lib.declare("DumpCheckMemory",
|
||||||
ctypes.default_abi,
|
ctypes.default_abi,
|
||||||
ctypes.bool,
|
ctypes.bool,
|
||||||
ctypes.char.ptr);
|
ctypes.char.ptr);
|
||||||
|
|
||||||
|
CrashTestUtils.getWin64CFITestFnAddrOffset =
|
||||||
|
lib.declare("GetWin64CFITestFnAddrOffset",
|
||||||
|
ctypes.default_abi,
|
||||||
|
ctypes.int32_t,
|
||||||
|
ctypes.int16_t);
|
||||||
|
|
|
@ -24,6 +24,11 @@ SOURCES += [
|
||||||
'ExceptionThrower.cpp',
|
'ExceptionThrower.cpp',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if CONFIG['OS_TARGET'] == 'WINNT' and CONFIG['CPU_ARCH'] == 'x86_64':
|
||||||
|
SOURCES += [
|
||||||
|
'win64UnwindInfoTests.asm',
|
||||||
|
]
|
||||||
|
|
||||||
if CONFIG['CLANG_CL']:
|
if CONFIG['CLANG_CL']:
|
||||||
SOURCES['ExceptionThrower.cpp'].flags += [
|
SOURCES['ExceptionThrower.cpp'].flags += [
|
||||||
'-Xclang',
|
'-Xclang',
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "ExceptionThrower.h"
|
#include "ExceptionThrower.h"
|
||||||
|
|
||||||
#ifdef XP_WIN
|
#ifdef XP_WIN
|
||||||
|
#include <malloc.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -43,6 +44,24 @@ void PureVirtualCall()
|
||||||
b.use(); // make sure b's actually used
|
b.use(); // make sure b's actually used
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#if XP_WIN && HAVE_64BIT_BUILD
|
||||||
|
// Implementation in win64unwindInfoTests.asm
|
||||||
|
uint64_t x64CrashCFITest_NO_MANS_LAND(uint64_t returnpfn, void*);
|
||||||
|
uint64_t x64CrashCFITest_Launcher(uint64_t returnpfn, void* testProc);
|
||||||
|
uint64_t x64CrashCFITest_UnknownOpcode(uint64_t returnpfn, void*);
|
||||||
|
uint64_t x64CrashCFITest_PUSH_NONVOL(uint64_t returnpfn, void*);
|
||||||
|
uint64_t x64CrashCFITest_ALLOC_SMALL(uint64_t returnpfn, void*);
|
||||||
|
uint64_t x64CrashCFITest_ALLOC_LARGE(uint64_t returnpfn, void*);
|
||||||
|
uint64_t x64CrashCFITest_SAVE_NONVOL(uint64_t returnpfn, void*);
|
||||||
|
uint64_t x64CrashCFITest_SAVE_NONVOL_FAR(uint64_t returnpfn, void*);
|
||||||
|
uint64_t x64CrashCFITest_SAVE_XMM128(uint64_t returnpfn, void*);
|
||||||
|
uint64_t x64CrashCFITest_SAVE_XMM128_FAR(uint64_t returnpfn, void*);
|
||||||
|
uint64_t x64CrashCFITest_EPILOG(uint64_t returnpfn, void*);
|
||||||
|
uint64_t x64CrashCFITest_EOF(uint64_t returnpfn, void*);
|
||||||
|
#endif // XP_WIN && HAVE_64BIT_BUILD
|
||||||
|
}
|
||||||
|
|
||||||
// Keep these in sync with CrashTestUtils.jsm!
|
// Keep these in sync with CrashTestUtils.jsm!
|
||||||
const int16_t CRASH_INVALID_POINTER_DEREF = 0;
|
const int16_t CRASH_INVALID_POINTER_DEREF = 0;
|
||||||
const int16_t CRASH_PURE_VIRTUAL_CALL = 1;
|
const int16_t CRASH_PURE_VIRTUAL_CALL = 1;
|
||||||
|
@ -50,6 +69,55 @@ const int16_t CRASH_OOM = 3;
|
||||||
const int16_t CRASH_MOZ_CRASH = 4;
|
const int16_t CRASH_MOZ_CRASH = 4;
|
||||||
const int16_t CRASH_ABORT = 5;
|
const int16_t CRASH_ABORT = 5;
|
||||||
const int16_t CRASH_UNCAUGHT_EXCEPTION = 6;
|
const int16_t CRASH_UNCAUGHT_EXCEPTION = 6;
|
||||||
|
const int16_t CRASH_X64CFI_NO_MANS_LAND = 7;
|
||||||
|
const int16_t CRASH_X64CFI_LAUNCHER = 8;
|
||||||
|
const int16_t CRASH_X64CFI_UNKNOWN_OPCODE = 9;
|
||||||
|
const int16_t CRASH_X64CFI_PUSH_NONVOL = 10;
|
||||||
|
const int16_t CRASH_X64CFI_ALLOC_SMALL = 11;
|
||||||
|
const int16_t CRASH_X64CFI_ALLOC_LARGE = 12;
|
||||||
|
const int16_t CRASH_X64CFI_SAVE_NONVOL = 15;
|
||||||
|
const int16_t CRASH_X64CFI_SAVE_NONVOL_FAR = 16;
|
||||||
|
const int16_t CRASH_X64CFI_SAVE_XMM128 = 17;
|
||||||
|
const int16_t CRASH_X64CFI_SAVE_XMM128_FAR = 18;
|
||||||
|
const int16_t CRASH_X64CFI_EPILOG = 19;
|
||||||
|
const int16_t CRASH_X64CFI_EOF = 20;
|
||||||
|
|
||||||
|
#if XP_WIN && HAVE_64BIT_BUILD
|
||||||
|
|
||||||
|
typedef decltype(&x64CrashCFITest_UnknownOpcode) win64CFITestFnPtr_t;
|
||||||
|
|
||||||
|
static std::map<int16_t, win64CFITestFnPtr_t>
|
||||||
|
GetWin64CFITestMap() {
|
||||||
|
std::map<int16_t, win64CFITestFnPtr_t> ret = {
|
||||||
|
{ CRASH_X64CFI_NO_MANS_LAND, x64CrashCFITest_NO_MANS_LAND},
|
||||||
|
{ CRASH_X64CFI_LAUNCHER, x64CrashCFITest_Launcher},
|
||||||
|
{ CRASH_X64CFI_UNKNOWN_OPCODE, x64CrashCFITest_UnknownOpcode},
|
||||||
|
{ CRASH_X64CFI_PUSH_NONVOL, x64CrashCFITest_PUSH_NONVOL},
|
||||||
|
{ CRASH_X64CFI_ALLOC_SMALL, x64CrashCFITest_ALLOC_SMALL },
|
||||||
|
{ CRASH_X64CFI_ALLOC_LARGE, x64CrashCFITest_ALLOC_LARGE },
|
||||||
|
{ CRASH_X64CFI_SAVE_NONVOL, x64CrashCFITest_SAVE_NONVOL },
|
||||||
|
{ CRASH_X64CFI_SAVE_NONVOL_FAR, x64CrashCFITest_SAVE_NONVOL_FAR },
|
||||||
|
{ CRASH_X64CFI_SAVE_XMM128, x64CrashCFITest_SAVE_XMM128 },
|
||||||
|
{ CRASH_X64CFI_SAVE_XMM128_FAR, x64CrashCFITest_SAVE_XMM128_FAR },
|
||||||
|
{ CRASH_X64CFI_EPILOG, x64CrashCFITest_EPILOG },
|
||||||
|
{ CRASH_X64CFI_EOF, x64CrashCFITest_EOF }
|
||||||
|
};
|
||||||
|
// ret values point to jump table entries, not the actual function bodies.
|
||||||
|
// Get the correct pointer by calling the function with returnpfn=1
|
||||||
|
for (auto it = ret.begin(); it != ret.end(); ++ it) {
|
||||||
|
it->second = (win64CFITestFnPtr_t)it->second(1, nullptr);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReserveStack() {
|
||||||
|
// This ensures our tests have enough reserved stack space.
|
||||||
|
uint8_t* p = (uint8_t*)alloca(1024000);
|
||||||
|
// This ensures we don't optimized away this meaningless code at build time.
|
||||||
|
mozilla::Unused << (int)(uint64_t)p;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // XP_WIN && HAVE_64BIT_BUILD
|
||||||
|
|
||||||
extern "C" NS_EXPORT
|
extern "C" NS_EXPORT
|
||||||
void Crash(int16_t how)
|
void Crash(int16_t how)
|
||||||
|
@ -84,6 +152,27 @@ void Crash(int16_t how)
|
||||||
ThrowException();
|
ThrowException();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#if XP_WIN && HAVE_64BIT_BUILD
|
||||||
|
case CRASH_X64CFI_UNKNOWN_OPCODE:
|
||||||
|
case CRASH_X64CFI_PUSH_NONVOL:
|
||||||
|
case CRASH_X64CFI_ALLOC_SMALL:
|
||||||
|
case CRASH_X64CFI_ALLOC_LARGE:
|
||||||
|
case CRASH_X64CFI_SAVE_NONVOL:
|
||||||
|
case CRASH_X64CFI_SAVE_NONVOL_FAR:
|
||||||
|
case CRASH_X64CFI_SAVE_XMM128:
|
||||||
|
case CRASH_X64CFI_SAVE_XMM128_FAR:
|
||||||
|
case CRASH_X64CFI_EPILOG: {
|
||||||
|
ReserveStack();
|
||||||
|
auto m = GetWin64CFITestMap();
|
||||||
|
if (m.find(how) == m.end()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto pfnTest = m[how];
|
||||||
|
auto pfnLauncher = m[CRASH_X64CFI_LAUNCHER];
|
||||||
|
pfnLauncher(0, pfnTest);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif // XP_WIN && HAVE_64BIT_BUILD
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -119,3 +208,20 @@ void TryOverrideExceptionHandler()
|
||||||
SetUnhandledExceptionFilter(HandleException);
|
SetUnhandledExceptionFilter(HandleException);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
extern "C" NS_EXPORT uint32_t
|
||||||
|
GetWin64CFITestFnAddrOffset(int16_t fnid) {
|
||||||
|
#if XP_WIN && HAVE_64BIT_BUILD
|
||||||
|
// fnid uses the same constants as Crash().
|
||||||
|
// Returns the RVA of the requested function.
|
||||||
|
// Returns 0 on failure.
|
||||||
|
auto m = GetWin64CFITestMap();
|
||||||
|
if (m.find(fnid) == m.end()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
uint64_t moduleBase = (uint64_t)GetModuleHandleW(L"testcrasher.dll");
|
||||||
|
return ((uint64_t)m[fnid]) - moduleBase;
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif // XP_WIN && HAVE_64BIT_BUILD
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
var {utils: Cu} = Components;
|
var {utils: Cu, classes: Cc, interfaces: Ci} = Components;
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/osfile.jsm");
|
Cu.import("resource://gre/modules/osfile.jsm");
|
||||||
Cu.import("resource://gre/modules/Services.jsm", this);
|
Cu.import("resource://gre/modules/Services.jsm", this);
|
||||||
Cu.import("resource://testing-common/AppData.jsm", this);
|
Cu.import("resource://testing-common/AppData.jsm", this);
|
||||||
|
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||||
|
|
||||||
function getEventDir() {
|
function getEventDir() {
|
||||||
return OS.Path.join(do_get_tempdir().path, "crash-events");
|
return OS.Path.join(do_get_tempdir().path, "crash-events");
|
||||||
|
@ -24,15 +25,17 @@ function getEventDir() {
|
||||||
*
|
*
|
||||||
* @param callback
|
* @param callback
|
||||||
* A JavaScript function to be called after the subprocess
|
* A JavaScript function to be called after the subprocess
|
||||||
* crashes. It will be passed (minidump, extra), where
|
* crashes. It will be passed (minidump, extra, extrafile), where
|
||||||
* minidump is an nsIFile of the minidump file produced,
|
* - minidump is an nsIFile of the minidump file produced,
|
||||||
* and extra is an object containing the key,value pairs from
|
* - extra is an object containing the key,value pairs from
|
||||||
* the .extra file.
|
* the .extra file.
|
||||||
|
* - extrafile is an nsIFile of the extra file
|
||||||
*
|
*
|
||||||
* @param canReturnZero
|
* @param canReturnZero
|
||||||
* If true, the subprocess may return with a zero exit code.
|
* If true, the subprocess may return with a zero exit code.
|
||||||
* Certain types of crashes may not cause the process to
|
* Certain types of crashes may not cause the process to
|
||||||
* exit with an error.
|
* exit with an error.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
function do_crash(setup, callback, canReturnZero) {
|
function do_crash(setup, callback, canReturnZero) {
|
||||||
// get current process filename (xpcshell)
|
// get current process filename (xpcshell)
|
||||||
|
@ -100,6 +103,32 @@ function getMinidump() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function runMinidumpAnalyzer(dumpFile, additionalArgs) {
|
||||||
|
if (AppConstants.platform !== "win") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find minidump-analyzer executable.
|
||||||
|
let ds = Cc["@mozilla.org/file/directory_service;1"]
|
||||||
|
.getService(Ci.nsIProperties);
|
||||||
|
let bin = ds.get("XREExeF", Ci.nsIFile);
|
||||||
|
ok(bin && bin.exists());
|
||||||
|
bin = bin.parent;
|
||||||
|
ok(bin && bin.exists());
|
||||||
|
bin.append("minidump-analyzer.exe");
|
||||||
|
ok(bin.exists());
|
||||||
|
|
||||||
|
let process = Cc["@mozilla.org/process/util;1"]
|
||||||
|
.createInstance(Ci.nsIProcess);
|
||||||
|
process.init(bin);
|
||||||
|
let args = [];
|
||||||
|
if (additionalArgs) {
|
||||||
|
args = args.concat(additionalArgs);
|
||||||
|
}
|
||||||
|
args.push(dumpFile.path);
|
||||||
|
process.run(true /* blocking */, args, args.length);
|
||||||
|
}
|
||||||
|
|
||||||
function handleMinidump(callback) {
|
function handleMinidump(callback) {
|
||||||
// find minidump
|
// find minidump
|
||||||
let minidump = getMinidump();
|
let minidump = getMinidump();
|
||||||
|
@ -131,7 +160,7 @@ function handleMinidump(callback) {
|
||||||
let extra = parseKeyValuePairsFromFile(extrafile);
|
let extra = parseKeyValuePairsFromFile(extrafile);
|
||||||
|
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(minidump, extra);
|
callback(minidump, extra, extrafile);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (minidump.exists()) {
|
if (minidump.exists()) {
|
||||||
|
|
|
@ -0,0 +1,193 @@
|
||||||
|
/* import-globals-from head_crashreporter.js */
|
||||||
|
|
||||||
|
let gTestCrasherSyms = null;
|
||||||
|
let gModules = null;
|
||||||
|
|
||||||
|
// Returns the offset (int) of an IP with a given base address.
|
||||||
|
// This is effectively (ip - base), except a bit more complication due to
|
||||||
|
// Javascript's shaky handling of 64-bit integers.
|
||||||
|
// base & ip are passed as hex strings.
|
||||||
|
function getModuleOffset(base, ip) {
|
||||||
|
let i = 0;
|
||||||
|
// Find where the two addresses diverge, which enables us to perform a 32-bit
|
||||||
|
// subtraction.
|
||||||
|
// e.g. "0x1111111111112222"
|
||||||
|
// - "0x1111111111111111"
|
||||||
|
// becomes 2222 - 1111
|
||||||
|
for (; i < base.length; ++i) {
|
||||||
|
if (base[i] != ip[i]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == base.length) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let lhs2 = "0x" + base.substring(i);
|
||||||
|
let rhs2 = "0x" + ip.substring(i);
|
||||||
|
return parseInt(rhs2) - parseInt(lhs2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uses gTestCrasherSyms to convert an address to a symbol.
|
||||||
|
function findNearestTestCrasherSymbol(addr) {
|
||||||
|
addr += 1; // Breakpad sometimes offsets addresses; correct for this.
|
||||||
|
let closestDistance = null;
|
||||||
|
let closestSym = null;
|
||||||
|
for (let sym in gTestCrasherSyms) {
|
||||||
|
if (addr >= gTestCrasherSyms[sym]) {
|
||||||
|
let thisDistance = addr - gTestCrasherSyms[sym];
|
||||||
|
if (closestDistance === null || thisDistance < closestDistance) {
|
||||||
|
closestDistance = thisDistance;
|
||||||
|
closestSym = sym;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (closestSym === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return { symbol: closestSym, offset: closestDistance }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate known symbols for testcrasher.dll.
|
||||||
|
// Use the same prop names as from CrashTestUtils to avoid the need for mapping.
|
||||||
|
function initTestCrasherSymbols() {
|
||||||
|
gTestCrasherSyms = { };
|
||||||
|
for (let k in CrashTestUtils) {
|
||||||
|
// Not all keys here are valid symbol names. getWin64CFITestFnAddrOffset
|
||||||
|
// will return 0 in those cases, no need to filter here.
|
||||||
|
if (Number.isInteger(CrashTestUtils[k])) {
|
||||||
|
let t = CrashTestUtils.getWin64CFITestFnAddrOffset(CrashTestUtils[k]);
|
||||||
|
if (t > 0) {
|
||||||
|
gTestCrasherSyms[k] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stackFrameToString(frameIndex, frame) {
|
||||||
|
// Calculate the module offset.
|
||||||
|
let ip = frame.ip;
|
||||||
|
let symbol = "";
|
||||||
|
let moduleOffset = "unknown_offset";
|
||||||
|
let filename = "unknown_module";
|
||||||
|
|
||||||
|
if (typeof frame.module_index !== "undefined" && (frame.module_index >= 0)
|
||||||
|
&& (frame.module_index < gModules.length)) {
|
||||||
|
|
||||||
|
let base = gModules[frame.module_index].base_addr;
|
||||||
|
moduleOffset = getModuleOffset(base, ip);
|
||||||
|
filename = gModules[frame.module_index].filename;
|
||||||
|
|
||||||
|
if (filename === "testcrasher.dll") {
|
||||||
|
let nearestSym = findNearestTestCrasherSymbol(moduleOffset);
|
||||||
|
if (nearestSym !== null) {
|
||||||
|
symbol = nearestSym.symbol + "+" + nearestSym.offset.toString(16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret = "frames[" + frameIndex + "] ip=" + ip +
|
||||||
|
" " + symbol +
|
||||||
|
", module:" + filename +
|
||||||
|
", trust:" + frame.trust +
|
||||||
|
", moduleOffset:" + moduleOffset.toString(16);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dumpStackFrames(frames, maxFrames) {
|
||||||
|
for (let i = 0; i < Math.min(maxFrames, frames.length); ++i) {
|
||||||
|
do_print(stackFrameToString(i, frames[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that the top of the given stack (from extra data) matches the given
|
||||||
|
// expected frames.
|
||||||
|
//
|
||||||
|
// expected is { symbol: "", trust: "" }
|
||||||
|
function assertStack(stack, expected) {
|
||||||
|
for (let i = 0; i < stack.length; ++i) {
|
||||||
|
if (i >= expected.length) {
|
||||||
|
ok("Top stack frames were expected");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let frame = stack[i];
|
||||||
|
let expectedFrame = expected[i];
|
||||||
|
let dumpThisFrame = function() {
|
||||||
|
do_print(" Actual frame: " + stackFrameToString(i, frame));
|
||||||
|
do_print("Expected { symbol: " + expectedFrame.symbol + ", trust: " + expectedFrame.trust + "}");
|
||||||
|
};
|
||||||
|
|
||||||
|
if (expectedFrame.trust) {
|
||||||
|
if (frame.trust !== expectedFrame.trust) {
|
||||||
|
dumpThisFrame();
|
||||||
|
do_print("Expected frame trust did not match.");
|
||||||
|
ok(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expectedFrame.symbol) {
|
||||||
|
if (typeof frame.module_index === "undefined") {
|
||||||
|
// Without a module_index, it happened in an unknown module. Currently
|
||||||
|
// you can't specify an expected "unknown" module.
|
||||||
|
do_print("Unknown symbol in unknown module.");
|
||||||
|
ok(false);
|
||||||
|
}
|
||||||
|
if (frame.module_index < 0 || frame.module_index >= gModules.length) {
|
||||||
|
dumpThisFrame();
|
||||||
|
do_print("Unknown module.");
|
||||||
|
ok(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let base = gModules[frame.module_index].base_addr;
|
||||||
|
let moduleOffset = getModuleOffset(base, frame.ip);
|
||||||
|
let filename = gModules[frame.module_index].filename;
|
||||||
|
if (filename == "testcrasher.dll") {
|
||||||
|
let nearestSym = findNearestTestCrasherSymbol(moduleOffset);
|
||||||
|
if (nearestSym === null) {
|
||||||
|
dumpThisFrame();
|
||||||
|
do_print("Unknown symbol.");
|
||||||
|
ok(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nearestSym.symbol !== expectedFrame.symbol) {
|
||||||
|
dumpThisFrame();
|
||||||
|
do_print("Mismatching symbol.");
|
||||||
|
ok(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Performs a crash, runs minidump-analyzer, and checks expected stack analysis.
|
||||||
|
//
|
||||||
|
// how: The crash to perform. Constants defined in both CrashTestUtils.jsm
|
||||||
|
// and nsTestCrasher.cpp (i.e. CRASH_X64CFI_PUSH_NONVOL)
|
||||||
|
// expectedStack: An array of {"symbol", "trust"} where trust is "cfi",
|
||||||
|
// "context", "scan", et al. May be null if you don't need to check the stack.
|
||||||
|
// minidumpAnalyzerArgs: An array of additional arguments to pass to
|
||||||
|
// minidump-analyzer.exe.
|
||||||
|
function do_x64CFITest(how, expectedStack, minidumpAnalyzerArgs) {
|
||||||
|
|
||||||
|
// Setup is run in the subprocess so we cannot use any closures.
|
||||||
|
let setupFn = "crashType = CrashTestUtils." + how + ";";
|
||||||
|
|
||||||
|
let callbackFn = function(minidumpFile, extra, extraFile) {
|
||||||
|
runMinidumpAnalyzer(minidumpFile, minidumpAnalyzerArgs);
|
||||||
|
|
||||||
|
// Refresh updated extra data
|
||||||
|
extra = parseKeyValuePairsFromFile(extraFile);
|
||||||
|
|
||||||
|
initTestCrasherSymbols();
|
||||||
|
let stackTraces = JSON.parse(extra.StackTraces);
|
||||||
|
let crashingThreadIndex = stackTraces.crash_info.crashing_thread;
|
||||||
|
gModules = stackTraces.modules;
|
||||||
|
let crashingFrames = stackTraces.threads[crashingThreadIndex].frames;
|
||||||
|
|
||||||
|
dumpStackFrames(crashingFrames, 10);
|
||||||
|
|
||||||
|
assertStack(crashingFrames, expectedStack);
|
||||||
|
};
|
||||||
|
|
||||||
|
do_crash(setupFn, callbackFn, true, true);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
|
||||||
|
function run_test() {
|
||||||
|
do_x64CFITest("CRASH_X64CFI_ALLOC_LARGE", [
|
||||||
|
{ symbol: "CRASH_X64CFI_ALLOC_LARGE", trust: "context" },
|
||||||
|
{ symbol: "CRASH_X64CFI_LAUNCHER", trust: "cfi" }
|
||||||
|
]);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
|
||||||
|
function run_test() {
|
||||||
|
do_x64CFITest("CRASH_X64CFI_ALLOC_SMALL", [
|
||||||
|
{ symbol: "CRASH_X64CFI_ALLOC_SMALL", trust: "context" },
|
||||||
|
{ symbol: "CRASH_X64CFI_LAUNCHER", trust: "cfi" }
|
||||||
|
]);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
|
||||||
|
function run_test() {
|
||||||
|
do_x64CFITest("CRASH_X64CFI_EPILOG", [
|
||||||
|
{ symbol: "CRASH_X64CFI_EPILOG", trust: "context" },
|
||||||
|
{ symbol: "CRASH_X64CFI_LAUNCHER", trust: "cfi" }
|
||||||
|
]);
|
||||||
|
}
|
Двоичные данные
toolkit/crashreporter/test/unit/test_crash_win64cfi_infinite_code_chain.exe
Normal file
Двоичные данные
toolkit/crashreporter/test/unit/test_crash_win64cfi_infinite_code_chain.exe
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
function run_test() {
|
||||||
|
// Test that minidump-analyzer gracefully handles chained
|
||||||
|
// unwind code entries that form a circular reference
|
||||||
|
// (infinite loop).
|
||||||
|
let exe = do_get_file("test_crash_win64cfi_infinite_code_chain.exe");
|
||||||
|
ok(exe);
|
||||||
|
|
||||||
|
// Perform a crash. We won't get unwind info, but make sure the stack scan
|
||||||
|
// still works.
|
||||||
|
do_x64CFITest("CRASH_X64CFI_ALLOC_SMALL",
|
||||||
|
[
|
||||||
|
{ symbol: "CRASH_X64CFI_ALLOC_SMALL", trust: "context" },
|
||||||
|
{ symbol: "CRASH_X64CFI_NO_MANS_LAND", trust: null }
|
||||||
|
],
|
||||||
|
["--force-use-module", exe.path]);
|
||||||
|
}
|
Двоичные данные
toolkit/crashreporter/test/unit/test_crash_win64cfi_infinite_entry_chain.exe
Normal file
Двоичные данные
toolkit/crashreporter/test/unit/test_crash_win64cfi_infinite_entry_chain.exe
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
function run_test() {
|
||||||
|
// Test that minidump-analyzer gracefully handles chained
|
||||||
|
// IMAGE_RUNTIME_FUNCTION_ENTRY items that form a circular reference
|
||||||
|
// (infinite loop).
|
||||||
|
let exe = do_get_file("test_crash_win64cfi_infinite_entry_chain.exe");
|
||||||
|
ok(exe);
|
||||||
|
|
||||||
|
// Perform a crash. We won't get unwind info, but make sure the stack scan
|
||||||
|
// still works.
|
||||||
|
do_x64CFITest("CRASH_X64CFI_ALLOC_SMALL",
|
||||||
|
[
|
||||||
|
{ symbol: "CRASH_X64CFI_ALLOC_SMALL", trust: "context" },
|
||||||
|
{ symbol: "CRASH_X64CFI_NO_MANS_LAND", trust: null }
|
||||||
|
],
|
||||||
|
["--force-use-module", exe.path]);
|
||||||
|
}
|
Двоичные данные
toolkit/crashreporter/test/unit/test_crash_win64cfi_invalid_exception_rva.exe
Normal file
Двоичные данные
toolkit/crashreporter/test/unit/test_crash_win64cfi_invalid_exception_rva.exe
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,16 @@
|
||||||
|
|
||||||
|
function run_test() {
|
||||||
|
// Test that minidump-analyzer gracefully handles an invalid pointer to the
|
||||||
|
// exception unwind information.
|
||||||
|
let exe = do_get_file("test_crash_win64cfi_invalid_exception_rva.exe");
|
||||||
|
ok(exe);
|
||||||
|
|
||||||
|
// Perform a crash. We won't get unwind info, but make sure the stack scan
|
||||||
|
// still works.
|
||||||
|
do_x64CFITest("CRASH_X64CFI_ALLOC_SMALL",
|
||||||
|
[
|
||||||
|
{ symbol: "CRASH_X64CFI_ALLOC_SMALL", trust: "context" },
|
||||||
|
{ symbol: "CRASH_X64CFI_NO_MANS_LAND", trust: null }
|
||||||
|
],
|
||||||
|
["--force-use-module", exe.path]);
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
this is not a valid PE file.
|
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
function run_test() {
|
||||||
|
// Test that minidump-analyzer gracefully handles corrupt PE files.
|
||||||
|
let exe = do_get_file("test_crash_win64cfi_not_a_pe.exe");
|
||||||
|
ok(exe);
|
||||||
|
|
||||||
|
// Perform a crash. We won't get unwind info, but make sure the stack scan
|
||||||
|
// still works.
|
||||||
|
do_x64CFITest("CRASH_X64CFI_ALLOC_SMALL",
|
||||||
|
[
|
||||||
|
{ symbol: "CRASH_X64CFI_ALLOC_SMALL", trust: "context" },
|
||||||
|
{ symbol: "CRASH_X64CFI_NO_MANS_LAND", trust: null }
|
||||||
|
],
|
||||||
|
["--force-use-module", exe.path]);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
|
||||||
|
function run_test() {
|
||||||
|
do_x64CFITest("CRASH_X64CFI_PUSH_NONVOL", [
|
||||||
|
{ symbol: "CRASH_X64CFI_PUSH_NONVOL", trust: "context" },
|
||||||
|
{ symbol: "CRASH_X64CFI_LAUNCHER", trust: "cfi" }
|
||||||
|
]);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
|
||||||
|
function run_test() {
|
||||||
|
do_x64CFITest("CRASH_X64CFI_SAVE_NONVOL", [
|
||||||
|
{ symbol: "CRASH_X64CFI_SAVE_NONVOL", trust: "context" },
|
||||||
|
{ symbol: "CRASH_X64CFI_LAUNCHER", trust: "cfi" }
|
||||||
|
]);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
|
||||||
|
function run_test() {
|
||||||
|
do_x64CFITest("CRASH_X64CFI_SAVE_NONVOL_FAR", [
|
||||||
|
{ symbol: "CRASH_X64CFI_SAVE_NONVOL_FAR", trust: "context" },
|
||||||
|
{ symbol: "CRASH_X64CFI_LAUNCHER", trust: "cfi" }
|
||||||
|
]);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
|
||||||
|
function run_test() {
|
||||||
|
do_x64CFITest("CRASH_X64CFI_SAVE_XMM128", [
|
||||||
|
{ symbol: "CRASH_X64CFI_SAVE_XMM128", trust: "context" },
|
||||||
|
{ symbol: "CRASH_X64CFI_LAUNCHER", trust: "cfi" }
|
||||||
|
]);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
|
||||||
|
function run_test() {
|
||||||
|
do_x64CFITest("CRASH_X64CFI_SAVE_XMM128_FAR", [
|
||||||
|
{ symbol: "CRASH_X64CFI_SAVE_XMM128_FAR", trust: "context" },
|
||||||
|
{ symbol: "CRASH_X64CFI_LAUNCHER", trust: "cfi" }
|
||||||
|
]);
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
|
||||||
|
function run_test() {
|
||||||
|
// In the case of an unknown unwind code or missing CFI,
|
||||||
|
// make certain we can still walk the stack via stack scan. The crashing
|
||||||
|
// function places NO_MANS_LAND on the stack so it will get picked up via
|
||||||
|
// stack scan.
|
||||||
|
do_x64CFITest("CRASH_X64CFI_UNKNOWN_OPCODE", [
|
||||||
|
{ symbol: "CRASH_X64CFI_UNKNOWN_OPCODE", trust: "context" },
|
||||||
|
// Trust may either be scan or frame_pointer; we don't really care as
|
||||||
|
// long as the address is expected.
|
||||||
|
{ symbol: "CRASH_X64CFI_NO_MANS_LAND", trust: null }
|
||||||
|
]);
|
||||||
|
}
|
|
@ -37,3 +37,65 @@ skip-if = (os != 'win' && os != 'linux') || (os=='linux' && bits==32)
|
||||||
[test_crash_AsyncShutdown.js]
|
[test_crash_AsyncShutdown.js]
|
||||||
[test_event_files.js]
|
[test_event_files.js]
|
||||||
[test_crash_terminator.js]
|
[test_crash_terminator.js]
|
||||||
|
|
||||||
|
[test_crash_win64cfi_unknown_op.js]
|
||||||
|
head = head_crashreporter.js head_win64cfi.js
|
||||||
|
skip-if = os != 'win' || bits != 64
|
||||||
|
|
||||||
|
[test_crash_win64cfi_push_nonvol.js]
|
||||||
|
head = head_crashreporter.js head_win64cfi.js
|
||||||
|
skip-if = os != 'win' || bits != 64
|
||||||
|
|
||||||
|
[test_crash_win64cfi_alloc_small.js]
|
||||||
|
head = head_crashreporter.js head_win64cfi.js
|
||||||
|
skip-if = os != 'win' || bits != 64
|
||||||
|
|
||||||
|
[test_crash_win64cfi_alloc_large.js]
|
||||||
|
head = head_crashreporter.js head_win64cfi.js
|
||||||
|
skip-if = os != 'win' || bits != 64
|
||||||
|
|
||||||
|
[test_crash_win64cfi_save_nonvol.js]
|
||||||
|
head = head_crashreporter.js head_win64cfi.js
|
||||||
|
skip-if = os != 'win' || bits != 64
|
||||||
|
|
||||||
|
[test_crash_win64cfi_save_nonvol_far.js]
|
||||||
|
head = head_crashreporter.js head_win64cfi.js
|
||||||
|
skip-if = os != 'win' || bits != 64
|
||||||
|
|
||||||
|
[test_crash_win64cfi_save_xmm128.js]
|
||||||
|
head = head_crashreporter.js head_win64cfi.js
|
||||||
|
skip-if = os != 'win' || bits != 64
|
||||||
|
|
||||||
|
[test_crash_win64cfi_save_xmm128_far.js]
|
||||||
|
head = head_crashreporter.js head_win64cfi.js
|
||||||
|
skip-if = os != 'win' || bits != 64
|
||||||
|
|
||||||
|
[test_crash_win64cfi_epilog.js]
|
||||||
|
head = head_crashreporter.js head_win64cfi.js
|
||||||
|
skip-if = os != 'win' || bits != 64
|
||||||
|
|
||||||
|
[test_crash_win64cfi_infinite_entry_chain.js]
|
||||||
|
head = head_crashreporter.js head_win64cfi.js
|
||||||
|
skip-if = os != 'win' || bits != 64
|
||||||
|
support-files = test_crash_win64cfi_infinite_entry_chain.exe
|
||||||
|
|
||||||
|
[test_crash_win64cfi_infinite_code_chain.js]
|
||||||
|
head = head_crashreporter.js head_win64cfi.js
|
||||||
|
skip-if = os != 'win' || bits != 64
|
||||||
|
support-files = test_crash_win64cfi_infinite_code_chain.exe
|
||||||
|
|
||||||
|
[test_crash_win64cfi_invalid_exception_rva.js]
|
||||||
|
head = head_crashreporter.js head_win64cfi.js
|
||||||
|
skip-if = os != 'win' || bits != 64
|
||||||
|
support-files = test_crash_win64cfi_invalid_exception_rva.exe
|
||||||
|
|
||||||
|
[test_crash_win64cfi_not_a_pe.js]
|
||||||
|
head = head_crashreporter.js head_win64cfi.js
|
||||||
|
skip-if = os != 'win' || bits != 64
|
||||||
|
support-files = test_crash_win64cfi_not_a_pe.exe
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,382 @@
|
||||||
|
; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
; Comments indicate stack memory layout during execution.
|
||||||
|
; For example at the top of a function, where RIP just points to the return
|
||||||
|
; address, the stack looks like
|
||||||
|
; rip = [ra]
|
||||||
|
; And after pushing rax to the stack,
|
||||||
|
; rip = [rax][ra]
|
||||||
|
; And then, after allocating 20h bytes on the stack,
|
||||||
|
; rip = [..20..][rax][ra]
|
||||||
|
; And then, after pushing a function pointer,
|
||||||
|
; rip = [pfn][..20..][rax][ra]
|
||||||
|
|
||||||
|
include ksamd64.inc
|
||||||
|
|
||||||
|
.code
|
||||||
|
|
||||||
|
; It helps to add padding between functions so they're not right up against
|
||||||
|
; each other. Adds clarity to debugging, and gives a bit of leeway when
|
||||||
|
; searching for symbols (e.g. a function whose last instruction is CALL
|
||||||
|
; would push a return address that's in the next function.)
|
||||||
|
PaddingBetweenFunctions macro
|
||||||
|
repeat 10h
|
||||||
|
int 3
|
||||||
|
endm
|
||||||
|
endm
|
||||||
|
|
||||||
|
DoCrash macro
|
||||||
|
mov rax, 7
|
||||||
|
mov byte ptr [rax], 9
|
||||||
|
endm
|
||||||
|
|
||||||
|
PaddingBetweenFunctions
|
||||||
|
|
||||||
|
; There is no rip addressing mode in x64. The only way to get the value
|
||||||
|
; of rip is to call a function, and pop it from the stack.
|
||||||
|
WhoCalledMe proc
|
||||||
|
pop rax ; rax is now ra
|
||||||
|
push rax ; Restore ra so this function can return.
|
||||||
|
sub rax, 5 ; Correct for the size of the call instruction
|
||||||
|
ret
|
||||||
|
WhoCalledMe endp
|
||||||
|
|
||||||
|
PaddingBetweenFunctions
|
||||||
|
|
||||||
|
; Any function that we expect to test against on the stack, we'll need its
|
||||||
|
; real address. If we use function pointers in C, we'll get the address to jump
|
||||||
|
; table entries. This bit of code at the beginning of each function will
|
||||||
|
; return the real address we'd expect to see in stack traces.
|
||||||
|
;
|
||||||
|
; rcx (1st arg) = mode
|
||||||
|
; rax (return) = address of either NO_MANS_LAND or this function.
|
||||||
|
;
|
||||||
|
; When mode is 0, we place the address of NO_MANS_LAND in RAX, for the function
|
||||||
|
; to use as it wants. This is just for convenience because almost all functions
|
||||||
|
; here need this address at some point.
|
||||||
|
;
|
||||||
|
; When mode is 1, the address of this function is returned.
|
||||||
|
TestHeader macro
|
||||||
|
call WhoCalledMe
|
||||||
|
test rcx, rcx
|
||||||
|
je continue_test
|
||||||
|
ret
|
||||||
|
continue_test:
|
||||||
|
inc rcx
|
||||||
|
call x64CrashCFITest_NO_MANS_LAND
|
||||||
|
xor rcx, rcx
|
||||||
|
endm
|
||||||
|
|
||||||
|
; The point of this is to add a stack frame to test against.
|
||||||
|
; void* x64CrashCFITest_Launcher(int getAddress, void* pTestFn)
|
||||||
|
x64CrashCFITest_Launcher proc frame
|
||||||
|
TestHeader
|
||||||
|
|
||||||
|
.endprolog
|
||||||
|
call rdx
|
||||||
|
ret
|
||||||
|
x64CrashCFITest_Launcher endp
|
||||||
|
|
||||||
|
PaddingBetweenFunctions
|
||||||
|
|
||||||
|
; void* x64CrashCFITest_NO_MANS_LAND(uint64_t mode);
|
||||||
|
; Not meant to be called. Only when mode = 1 in order to return its address.
|
||||||
|
; Place this function's address on the stack so the stack scanning algorithm
|
||||||
|
; thinks this is a return address, and places it on the stack trace.
|
||||||
|
x64CrashCFITest_NO_MANS_LAND proc frame
|
||||||
|
TestHeader
|
||||||
|
.endprolog
|
||||||
|
ret
|
||||||
|
x64CrashCFITest_NO_MANS_LAND endp
|
||||||
|
|
||||||
|
PaddingBetweenFunctions
|
||||||
|
|
||||||
|
; Test that we:
|
||||||
|
; - handle unknown opcodes gracefully
|
||||||
|
; - fall back to other stack unwind strategies if CFI doesn't work
|
||||||
|
;
|
||||||
|
; In order to properly unwind this frame, we'd need to fully support
|
||||||
|
; SET_FPREG with offsets, plus restoring registers via PUSH_NONVOL.
|
||||||
|
; To do this, sprinkle the stack with bad return addresses
|
||||||
|
; and stack pointers.
|
||||||
|
x64CrashCFITest_UnknownOpcode proc frame
|
||||||
|
TestHeader
|
||||||
|
|
||||||
|
push rax
|
||||||
|
.allocstack 8
|
||||||
|
|
||||||
|
push rbp
|
||||||
|
.pushreg rbp
|
||||||
|
|
||||||
|
push rax
|
||||||
|
push rsp
|
||||||
|
push rax
|
||||||
|
push rsp
|
||||||
|
.allocstack 20h
|
||||||
|
; rsp = [rsp][pfn][rsp][pfn][rbp][pfn][ra]
|
||||||
|
|
||||||
|
lea rbp, [rsp+10h]
|
||||||
|
.setframe rbp, 10h
|
||||||
|
; rsp = [rsp][pfn] [rsp][pfn][rbp][pfn][ra]
|
||||||
|
; rbp = ^
|
||||||
|
|
||||||
|
.endprolog
|
||||||
|
|
||||||
|
; Now modify RSP so measuring stack size from unwind ops will not help
|
||||||
|
; finding the return address.
|
||||||
|
push rax
|
||||||
|
push rsp
|
||||||
|
; rsp = [rsp][pfn][rsp][pfn] [rsp][pfn][rbp][pfn][ra]
|
||||||
|
|
||||||
|
DoCrash
|
||||||
|
|
||||||
|
x64CrashCFITest_UnknownOpcode endp
|
||||||
|
|
||||||
|
PaddingBetweenFunctions
|
||||||
|
|
||||||
|
; void* x64CrashCFITest_PUSH_NONVOL(uint64_t mode);
|
||||||
|
;
|
||||||
|
; Test correct handling of PUSH_NONVOL unwind code.
|
||||||
|
;
|
||||||
|
x64CrashCFITest_PUSH_NONVOL proc frame
|
||||||
|
TestHeader
|
||||||
|
|
||||||
|
push r10
|
||||||
|
.pushreg r10
|
||||||
|
push r15
|
||||||
|
.pushreg r15
|
||||||
|
push rbx
|
||||||
|
.pushreg rbx
|
||||||
|
push rsi
|
||||||
|
.pushreg rsi
|
||||||
|
push rbp
|
||||||
|
.pushreg rbp
|
||||||
|
; rsp = [rbp][rsi][rbx][r15][r10][ra]
|
||||||
|
|
||||||
|
push rax
|
||||||
|
.allocstack 8
|
||||||
|
; rsp = [pfn][rbp][rsi][rbx][r15][r10][ra]
|
||||||
|
|
||||||
|
.endprolog
|
||||||
|
|
||||||
|
DoCrash
|
||||||
|
|
||||||
|
x64CrashCFITest_PUSH_NONVOL endp
|
||||||
|
|
||||||
|
PaddingBetweenFunctions
|
||||||
|
|
||||||
|
; void* x64CrashCFITest_ALLOC_SMALL(uint64_t mode);
|
||||||
|
;
|
||||||
|
; Small allocations are between 8bytes and 512kb-8bytes
|
||||||
|
;
|
||||||
|
x64CrashCFITest_ALLOC_SMALL proc frame
|
||||||
|
TestHeader
|
||||||
|
|
||||||
|
; Trash rbp to force stack scan. This will force
|
||||||
|
; correct behavior for test_crash_win64cfi_not_a_pe, et al.
|
||||||
|
xor rbp, rbp
|
||||||
|
|
||||||
|
push rax
|
||||||
|
push rax
|
||||||
|
push rax
|
||||||
|
push rax
|
||||||
|
.allocstack 20h
|
||||||
|
; rsp = [pfn][pfn][pfn][pfn][ra]
|
||||||
|
|
||||||
|
.endprolog
|
||||||
|
|
||||||
|
DoCrash
|
||||||
|
|
||||||
|
x64CrashCFITest_ALLOC_SMALL endp
|
||||||
|
|
||||||
|
PaddingBetweenFunctions
|
||||||
|
|
||||||
|
; void* x64CrashCFITest_ALLOC_LARGE(uint64_t mode);
|
||||||
|
;
|
||||||
|
; Allocations between 512kb and 4gb
|
||||||
|
; Note: ReserveStackSpace() in nsTestCrasher.cpp pre-allocates stack
|
||||||
|
; space for this.
|
||||||
|
x64CrashCFITest_ALLOC_LARGE proc frame
|
||||||
|
TestHeader
|
||||||
|
|
||||||
|
sub rsp, 0a000h
|
||||||
|
.allocstack 0a000h
|
||||||
|
; rsp = [..640kb..][ra]
|
||||||
|
|
||||||
|
mov qword ptr [rsp], rax
|
||||||
|
; rsp = [pfn][..640kb-8..][ra]
|
||||||
|
|
||||||
|
.endprolog
|
||||||
|
|
||||||
|
DoCrash
|
||||||
|
|
||||||
|
x64CrashCFITest_ALLOC_LARGE endp
|
||||||
|
|
||||||
|
PaddingBetweenFunctions
|
||||||
|
|
||||||
|
; void* x64CrashCFITest_SAVE_NONVOL(uint64_t mode);
|
||||||
|
;
|
||||||
|
; Test correct handling of SAVE_NONVOL unwind code.
|
||||||
|
;
|
||||||
|
x64CrashCFITest_SAVE_NONVOL proc frame
|
||||||
|
TestHeader
|
||||||
|
|
||||||
|
sub rsp, 30h
|
||||||
|
.allocstack 30h
|
||||||
|
; rsp = [..30..][ra]
|
||||||
|
|
||||||
|
mov qword ptr [rsp+28h], r10
|
||||||
|
.savereg r10, 28h
|
||||||
|
mov qword ptr [rsp+20h], rbp
|
||||||
|
.savereg rbp, 20h
|
||||||
|
mov qword ptr [rsp+18h], rsi
|
||||||
|
.savereg rsi, 18h
|
||||||
|
mov qword ptr [rsp+10h], rbx
|
||||||
|
.savereg rbx, 10h
|
||||||
|
mov qword ptr [rsp+8], r15
|
||||||
|
.savereg r15, 8
|
||||||
|
; rsp = [r15][rbx][rsi][rbp][r10][ra]
|
||||||
|
|
||||||
|
mov qword ptr [rsp], rax
|
||||||
|
|
||||||
|
; rsp = [pfn][r15][rbx][rsi][rbp][r10][ra]
|
||||||
|
|
||||||
|
.endprolog
|
||||||
|
|
||||||
|
DoCrash
|
||||||
|
|
||||||
|
x64CrashCFITest_SAVE_NONVOL endp
|
||||||
|
|
||||||
|
PaddingBetweenFunctions
|
||||||
|
|
||||||
|
; void* x64CrashCFITest_SAVE_NONVOL_FAR(uint64_t mode);
|
||||||
|
;
|
||||||
|
; Similar to the test above but adding 640kb to most offsets.
|
||||||
|
; Note: ReserveStackSpace() in nsTestCrasher.cpp pre-allocates stack
|
||||||
|
; space for this.
|
||||||
|
x64CrashCFITest_SAVE_NONVOL_FAR proc frame
|
||||||
|
TestHeader
|
||||||
|
|
||||||
|
sub rsp, 0a0030h
|
||||||
|
.allocstack 0a0030h
|
||||||
|
; rsp = [..640k..][..30..][ra]
|
||||||
|
|
||||||
|
mov qword ptr [rsp+28h+0a0000h], r10
|
||||||
|
.savereg r10, 28h+0a0000h
|
||||||
|
mov qword ptr [rsp+20h+0a0000h], rbp
|
||||||
|
.savereg rbp, 20h+0a0000h
|
||||||
|
mov qword ptr [rsp+18h+0a0000h], rsi
|
||||||
|
.savereg rsi, 18h+0a0000h
|
||||||
|
mov qword ptr [rsp+10h+0a0000h], rbx
|
||||||
|
.savereg rbx, 10h+0a0000h
|
||||||
|
mov qword ptr [rsp+8+0a0000h], r15
|
||||||
|
.savereg r15, 8+0a0000h
|
||||||
|
; rsp = [..640k..][..8..][r15][rbx][rsi][rbp][r10][ra]
|
||||||
|
|
||||||
|
mov qword ptr [rsp], rax
|
||||||
|
|
||||||
|
; rsp = [pfn][..640k..][r15][rbx][rsi][rbp][r10][ra]
|
||||||
|
|
||||||
|
.endprolog
|
||||||
|
|
||||||
|
DoCrash
|
||||||
|
|
||||||
|
x64CrashCFITest_SAVE_NONVOL_FAR endp
|
||||||
|
|
||||||
|
PaddingBetweenFunctions
|
||||||
|
|
||||||
|
; void* x64CrashCFITest_SAVE_XMM128(uint64_t mode);
|
||||||
|
;
|
||||||
|
; Test correct handling of SAVE_XMM128 unwind code.
|
||||||
|
x64CrashCFITest_SAVE_XMM128 proc frame
|
||||||
|
TestHeader
|
||||||
|
|
||||||
|
sub rsp, 30h
|
||||||
|
.allocstack 30h
|
||||||
|
; rsp = [..30..][ra]
|
||||||
|
|
||||||
|
movdqu [rsp+20h], xmm6
|
||||||
|
.savexmm128 xmm6, 20h
|
||||||
|
; rsp = [..20..][xmm6][ra]
|
||||||
|
|
||||||
|
movdqu [rsp+10h], xmm15
|
||||||
|
.savexmm128 xmm15, 10h
|
||||||
|
; rsp = [..10..][xmm15][xmm6][ra]
|
||||||
|
|
||||||
|
mov qword ptr [rsp], rax
|
||||||
|
; rsp = [pfn][..8..][xmm15][xmm6][ra]
|
||||||
|
|
||||||
|
.endprolog
|
||||||
|
|
||||||
|
DoCrash
|
||||||
|
|
||||||
|
x64CrashCFITest_SAVE_XMM128 endp
|
||||||
|
|
||||||
|
PaddingBetweenFunctions
|
||||||
|
|
||||||
|
; void* x64CrashCFITest_SAVE_XMM128(uint64_t mode);
|
||||||
|
;
|
||||||
|
; Similar to the test above but adding 640kb to most offsets.
|
||||||
|
; Note: ReserveStackSpace() in nsTestCrasher.cpp pre-allocates stack
|
||||||
|
; space for this.
|
||||||
|
x64CrashCFITest_SAVE_XMM128_FAR proc frame
|
||||||
|
TestHeader
|
||||||
|
|
||||||
|
sub rsp, 0a0030h
|
||||||
|
.allocstack 0a0030h
|
||||||
|
; rsp = [..640kb..][..30..][ra]
|
||||||
|
|
||||||
|
movdqu [rsp+20h+0a0000h], xmm6
|
||||||
|
.savexmm128 xmm6, 20h+0a0000h
|
||||||
|
; rsp = [..640kb..][..20..][xmm6][ra]
|
||||||
|
|
||||||
|
movdqu [rsp+10h+0a0000h], xmm6
|
||||||
|
.savexmm128 xmm15, 10h+0a0000h
|
||||||
|
; rsp = [..640kb..][..10..][xmm15][xmm6][ra]
|
||||||
|
|
||||||
|
mov qword ptr [rsp], rax
|
||||||
|
; rsp = [pfn][..640kb..][..8..][xmm15][xmm6][ra]
|
||||||
|
|
||||||
|
.endprolog
|
||||||
|
|
||||||
|
DoCrash
|
||||||
|
|
||||||
|
x64CrashCFITest_SAVE_XMM128_FAR endp
|
||||||
|
|
||||||
|
PaddingBetweenFunctions
|
||||||
|
|
||||||
|
; void* x64CrashCFITest_EPILOG(uint64_t mode);
|
||||||
|
;
|
||||||
|
; The epilog unwind op will also set the unwind version to 2.
|
||||||
|
; Test that we don't choke on UWOP_EPILOG or version 2 unwind info.
|
||||||
|
x64CrashCFITest_EPILOG proc frame
|
||||||
|
TestHeader
|
||||||
|
|
||||||
|
push rax
|
||||||
|
.allocstack 8
|
||||||
|
; rsp = [pfn][ra]
|
||||||
|
|
||||||
|
.endprolog
|
||||||
|
|
||||||
|
DoCrash
|
||||||
|
|
||||||
|
.beginepilog
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
x64CrashCFITest_EPILOG endp
|
||||||
|
|
||||||
|
PaddingBetweenFunctions
|
||||||
|
|
||||||
|
; Having an EOF symbol at the end of this file contains symbolication to this
|
||||||
|
; file. So addresses beyond this file don't get mistakenly symbolicated as a
|
||||||
|
; meaningful function name.
|
||||||
|
x64CrashCFITest_EOF proc frame
|
||||||
|
TestHeader
|
||||||
|
.endprolog
|
||||||
|
ret
|
||||||
|
x64CrashCFITest_EOF endp
|
||||||
|
|
||||||
|
end
|
|
@ -107,7 +107,8 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Linux builds use LUL, which uses DWARF info to unwind stacks.
|
// Linux builds use LUL, which uses DWARF info to unwind stacks.
|
||||||
#if defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_x86_linux) || defined(GP_PLAT_mips64_linux)
|
#if defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_x86_linux) || \
|
||||||
|
defined(GP_PLAT_mips64_linux)
|
||||||
# define HAVE_NATIVE_UNWIND
|
# define HAVE_NATIVE_UNWIND
|
||||||
# define USE_LUL_STACKWALK
|
# define USE_LUL_STACKWALK
|
||||||
# include "lul/LulMain.h"
|
# include "lul/LulMain.h"
|
||||||
|
@ -1133,6 +1134,10 @@ DoLULBacktrace(PSLockRef aLock, const ThreadInfo& aThreadInfo,
|
||||||
startRegs.xip = lul::TaggedUWord(mc->gregs[REG_EIP]);
|
startRegs.xip = lul::TaggedUWord(mc->gregs[REG_EIP]);
|
||||||
startRegs.xsp = lul::TaggedUWord(mc->gregs[REG_ESP]);
|
startRegs.xsp = lul::TaggedUWord(mc->gregs[REG_ESP]);
|
||||||
startRegs.xbp = lul::TaggedUWord(mc->gregs[REG_EBP]);
|
startRegs.xbp = lul::TaggedUWord(mc->gregs[REG_EBP]);
|
||||||
|
#elif defined(GP_PLAT_mips64_linux)
|
||||||
|
startRegs.pc = lul::TaggedUWord(mc->pc);
|
||||||
|
startRegs.sp = lul::TaggedUWord(mc->gregs[29]);
|
||||||
|
startRegs.fp = lul::TaggedUWord(mc->gregs[30]);
|
||||||
#else
|
#else
|
||||||
# error "Unknown plat"
|
# error "Unknown plat"
|
||||||
#endif
|
#endif
|
||||||
|
@ -1180,6 +1185,9 @@ DoLULBacktrace(PSLockRef aLock, const ThreadInfo& aThreadInfo,
|
||||||
#elif defined(GP_PLAT_x86_linux) || defined(GP_PLAT_x86_android)
|
#elif defined(GP_PLAT_x86_linux) || defined(GP_PLAT_x86_android)
|
||||||
uintptr_t rEDZONE_SIZE = 0;
|
uintptr_t rEDZONE_SIZE = 0;
|
||||||
uintptr_t start = startRegs.xsp.Value() - rEDZONE_SIZE;
|
uintptr_t start = startRegs.xsp.Value() - rEDZONE_SIZE;
|
||||||
|
#elif defined(GP_PLAT_mips64_linux)
|
||||||
|
uintptr_t rEDZONE_SIZE = 0;
|
||||||
|
uintptr_t start = startRegs.sp.Value() - rEDZONE_SIZE;
|
||||||
#else
|
#else
|
||||||
# error "Unknown plat"
|
# error "Unknown plat"
|
||||||
#endif
|
#endif
|
||||||
|
|
Загрузка…
Ссылка в новой задаче