Merge inbound to mozilla-central r=merge a=merge

This commit is contained in:
Noemi Erli 2017-11-15 11:57:12 +02:00
Родитель 7f5c3c3582 29099d481c
Коммит 550148ab69
81 изменённых файлов: 3686 добавлений и 1297 удалений

Просмотреть файл

@ -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" }
]);
}

Двоичный файл не отображается.

Просмотреть файл

@ -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]);
}

Двоичный файл не отображается.

Просмотреть файл

@ -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]);
}

Двоичный файл не отображается.

Просмотреть файл

@ -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