Merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Razvan Maries 2019-07-23 00:37:54 +03:00
Родитель e691fe7a86 19aca80c51
Коммит 86980b4bb2
71 изменённых файлов: 698 добавлений и 259 удалений

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

@ -488,7 +488,7 @@ class Tree extends Component {
super(props);
this.state = {
seen: new Set(),
autoExpanded: new Set(),
};
this.treeRef = React.createRef();
@ -536,29 +536,40 @@ class Tree extends Component {
}
_autoExpand() {
const { autoExpandDepth, autoExpandNodeChildrenLimit } = this.props;
if (!autoExpandDepth) {
const {
autoExpandDepth,
autoExpandNodeChildrenLimit,
initiallyExpanded,
} = this.props;
if (!autoExpandDepth && !initiallyExpanded) {
return;
}
// Automatically expand the first autoExpandDepth levels for new items. Do
// not use the usual DFS infrastructure because we don't want to ignore
// collapsed nodes.
// collapsed nodes. Any initially expanded items will be expanded regardless
// of how deep they are.
const autoExpand = (item, currentDepth) => {
if (currentDepth >= autoExpandDepth || this.state.seen.has(item)) {
const initial = initiallyExpanded && initiallyExpanded(item);
if (!initial && currentDepth >= autoExpandDepth) {
return;
}
const children = this.props.getChildren(item);
if (
!initial &&
autoExpandNodeChildrenLimit &&
children.length > autoExpandNodeChildrenLimit
) {
return;
}
this.props.onExpand(item);
this.state.seen.add(item);
if (!this.state.autoExpanded.has(item)) {
this.props.onExpand(item);
this.state.autoExpanded.add(item);
}
const length = children.length;
for (let i = 0; i < length; i++) {
@ -574,6 +585,14 @@ class Tree extends Component {
}
} else if (length != 0) {
autoExpand(roots[0], 0);
if (initiallyExpanded) {
for (let i = 1; i < length; i++) {
if (initiallyExpanded(roots[i])) {
autoExpand(roots[i], 0);
}
}
}
}
}

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

@ -198,6 +198,7 @@ class ObjectInspector extends Component<Props> {
nodeExpand,
nodeCollapse,
recordTelemetryEvent,
setExpanded,
roots,
} = this.props;
@ -210,6 +211,10 @@ class ObjectInspector extends Component<Props> {
} else {
nodeCollapse(item);
}
if (setExpanded) {
setExpanded(item, expand);
}
}
focusItem(item: Node) {
@ -249,6 +254,7 @@ class ObjectInspector extends Component<Props> {
const {
autoExpandAll = true,
autoExpandDepth = 1,
initiallyExpanded,
focusable = true,
disableWrap = false,
expandedPaths,
@ -264,6 +270,7 @@ class ObjectInspector extends Component<Props> {
autoExpandAll,
autoExpandDepth,
initiallyExpanded,
isExpanded: item => expandedPaths && expandedPaths.has(item.path),
isExpandable: this.isNodeExpandable,

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

@ -0,0 +1,25 @@
/* 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/>. */
// @flow
import { getScopeItemPath } from "../../utils/pause/scopes/utils";
import type { ThunkArgs } from "../types";
import type { ThreadContext } from "../../types";
export function setExpandedScope(
cx: ThreadContext,
item: Object,
expanded: boolean
) {
return function({ dispatch, getState }: ThunkArgs) {
return dispatch({
type: "SET_EXPANDED_SCOPE",
cx,
thread: cx.thread,
path: getScopeItemPath(item),
expanded,
});
};
}

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

@ -28,3 +28,4 @@ export { pauseOnExceptions } from "./pauseOnExceptions";
export { selectFrame } from "./selectFrame";
export { toggleSkipPausing } from "./skipPausing";
export { toggleMapScopes } from "./mapScopes";
export { setExpandedScope } from "./expandScopes";

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

@ -11,6 +11,7 @@ CompiledModules(
'breakOnNext.js',
'commands.js',
'continueToHere.js',
'expandScopes.js',
'fetchScopes.js',
'index.js',
'mapFrames.js',

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

@ -147,4 +147,11 @@ export type PauseAction =
| {|
+type: "TOGGLE_MAP_SCOPES",
+mapScopes: boolean,
|}
| {|
+type: "SET_EXPANDED_SCOPE",
+cx: ThreadContext,
+thread: string,
+path: string,
+expanded: boolean,
|};

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

@ -13,17 +13,18 @@ import {
getSelectedFrame,
getGeneratedFrameScope,
getOriginalFrameScope,
getIsPaused,
getPauseReason,
isMapScopesEnabled,
getCurrentThread,
getThreadContext,
getLastExpandedScopes,
} from "../../selectors";
import { getScopes } from "../../utils/pause/scopes";
import { getScopeItemPath } from "../../utils/pause/scopes/utils";
// eslint-disable-next-line import/named
import { objectInspector } from "devtools-reps";
import type { Why } from "../../types";
import type { ThreadContext, Why } from "../../types";
import type { NamedValue } from "../../utils/pause/scopes/types";
import "./Scopes.css";
@ -31,7 +32,7 @@ import "./Scopes.css";
const { ObjectInspector } = objectInspector;
type Props = {
isPaused: boolean,
cx: ThreadContext,
selectedFrame: Object,
generatedFrameScopes: Object,
originalFrameScopes: Object | null,
@ -43,6 +44,8 @@ type Props = {
highlightDomElement: typeof actions.highlightDomElement,
unHighlightDomElement: typeof actions.unHighlightDomElement,
toggleMapScopes: typeof actions.toggleMapScopes,
setExpandedScope: typeof actions.setExpandedScope,
expandedScopes: string[],
};
type State = {
@ -71,12 +74,12 @@ class Scopes extends PureComponent<Props, State> {
componentWillReceiveProps(nextProps) {
const {
isPaused,
cx,
selectedFrame,
originalFrameScopes,
generatedFrameScopes,
} = this.props;
const isPausedChanged = isPaused !== nextProps.isPaused;
const isPausedChanged = cx.isPaused !== nextProps.cx.isPaused;
const selectedFrameChanged = selectedFrame !== nextProps.selectedFrame;
const originalFrameScopesChanged =
originalFrameScopes !== nextProps.originalFrameScopes;
@ -110,19 +113,25 @@ class Scopes extends PureComponent<Props, State> {
renderScopesList() {
const {
isPaused,
cx,
isLoading,
openLink,
openElementInInspector,
highlightDomElement,
unHighlightDomElement,
mapScopesEnabled,
setExpandedScope,
expandedScopes,
} = this.props;
const { originalScopes, generatedScopes, showOriginal } = this.state;
const scopes =
(showOriginal && mapScopesEnabled && originalScopes) || generatedScopes;
function initiallyExpanded(item) {
return expandedScopes.some(path => path == getScopeItemPath(item));
}
if (scopes && scopes.length > 0 && !isLoading) {
return (
<div className="pane scopes-list">
@ -138,13 +147,15 @@ class Scopes extends PureComponent<Props, State> {
onInspectIconClick={grip => openElementInInspector(grip)}
onDOMNodeMouseOver={grip => highlightDomElement(grip)}
onDOMNodeMouseOut={grip => unHighlightDomElement(grip)}
setExpanded={(path, expand) => setExpandedScope(cx, path, expand)}
initiallyExpanded={initiallyExpanded}
/>
</div>
);
}
let stateText = L10N.getStr("scopes.notPaused");
if (isPaused) {
if (cx.isPaused) {
if (isLoading) {
stateText = L10N.getStr("loadingText");
} else {
@ -165,8 +176,8 @@ class Scopes extends PureComponent<Props, State> {
}
const mapStateToProps = state => {
const thread = getCurrentThread(state);
const selectedFrame = getSelectedFrame(state, thread);
const cx = getThreadContext(state);
const selectedFrame = getSelectedFrame(state, cx.thread);
const selectedSource = getSelectedSource(state);
const {
@ -174,7 +185,7 @@ const mapStateToProps = state => {
pending: originalPending,
} = getOriginalFrameScope(
state,
thread,
cx.thread,
selectedSource && selectedSource.id,
selectedFrame && selectedFrame.id
) || { scope: null, pending: false };
@ -184,7 +195,7 @@ const mapStateToProps = state => {
pending: generatedPending,
} = getGeneratedFrameScope(
state,
thread,
cx.thread,
selectedFrame && selectedFrame.id
) || {
scope: null,
@ -192,13 +203,14 @@ const mapStateToProps = state => {
};
return {
cx,
selectedFrame,
mapScopesEnabled: isMapScopesEnabled(state),
isPaused: getIsPaused(state, thread),
isLoading: generatedPending || originalPending,
why: getPauseReason(state, thread),
why: getPauseReason(state, cx.thread),
originalFrameScopes,
generatedFrameScopes,
expandedScopes: getLastExpandedScopes(state, cx.thread),
};
};
@ -210,5 +222,6 @@ export default connect(
highlightDomElement: actions.highlightDomElement,
unHighlightDomElement: actions.unHighlightDomElement,
toggleMapScopes: actions.toggleMapScopes,
setExpandedScope: actions.setExpandedScope,
}
)(Scopes);

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

@ -64,6 +64,18 @@ type ThreadPauseState = {
},
},
selectedFrameId: ?string,
// Scope items that have been expanded in the current pause.
expandedScopes: Set<string>,
// Scope items that were expanded in the last pause. This is separate from
// expandedScopes so that (a) the scope pane's ObjectInspector does not depend
// on the current expanded scopes and we don't have to re-render the entire
// ObjectInspector when an element is expanded or collapsed, and (b) so that
// the expanded scopes are regenerated when we pause at a new location and we
// don't have to worry about pruning obsolete scope entries.
lastExpandedScopes: string[],
command: Command,
lastCommand: Command,
wasStepping: boolean,
@ -120,6 +132,8 @@ const createInitialPauseState = () => ({
command: null,
lastCommand: null,
previousLocation: null,
expandedScopes: new Set(),
lastExpandedScopes: [],
});
function getThreadPauseState(state: PauseState, thread: ThreadId) {
@ -292,6 +306,8 @@ function update(
return updateThreadState({
...resumedPauseState,
wasStepping: !!action.wasStepping,
expandedScopes: new Set(),
lastExpandedScopes: [...threadState().expandedScopes],
});
}
@ -315,7 +331,7 @@ function update(
},
threads: {
[action.mainThread.actor]: {
...state.threads[action.mainThread.actor],
...getThreadPauseState(state, action.mainThread.actor),
...resumedPauseState,
},
},
@ -334,6 +350,17 @@ function update(
prefs.mapScopes = mapScopes;
return { ...state, mapScopes };
}
case "SET_EXPANDED_SCOPE": {
const { path, expanded } = action;
const expandedScopes = new Set(threadState().expandedScopes);
if (expanded) {
expandedScopes.add(path);
} else {
expandedScopes.delete(path);
}
return updateThreadState({ expandedScopes });
}
}
return state;
@ -582,4 +609,8 @@ export function getChromeScopes(state: State, thread: ThreadId) {
return frame ? frame.scopeChain : undefined;
}
export function getLastExpandedScopes(state: State, thread: ThreadId) {
return getThreadPauseState(state.pause, thread).lastExpandedScopes;
}
export default update;

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

@ -51,3 +51,10 @@ export function getThisVariable(this_: any, path: string): ?NamedValue {
contents: { value: this_ },
};
}
// Get a string path for an scope item which can be used in different pauses for
// a thread.
export function getScopeItemPath(item: Object) {
// Calling toString() on item.path allows symbols to be handled.
return item.path.toString();
}

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

@ -150,3 +150,4 @@ skip-if = (os == 'linux' && debug) || (os == 'linux' && asan) || ccov #Bug 1456
[browser_dbg-scopes-xrays.js]
[browser_dbg-merge-scopes.js]
[browser_dbg-message-run-to-completion.js]
[browser_dbg-remember-expanded-scopes.js]

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

@ -10,8 +10,8 @@ function waitForInspectorPanelChange(dbg) {
return new Promise(resolve => {
toolbox.getPanelWhenReady("inspector").then(() => {
ok(toolbox.inspector, "Inspector is shown.");
resolve(toolbox.inspector);
ok(toolbox.inspectorFront, "Inspector is shown.");
resolve(toolbox.inspectorFront);
});
});
}
@ -31,9 +31,14 @@ add_task(async function() {
// Ensure hovering over button highlights the node in content pane
const view = inspectorNode.ownerDocument.defaultView;
const onNodeHighlight = toolbox.target.once("inspector")
const onNodeHighlight = toolbox.target
.once("inspector")
.then(inspector => inspector.highlighter.once("node-highlight"));
EventUtils.synthesizeMouseAtCenter(inspectorNode, {type: "mousemove"}, view);
EventUtils.synthesizeMouseAtCenter(
inspectorNode,
{ type: "mousemove" },
view
);
const nodeFront = await onNodeHighlight;
is(nodeFront.displayName, "button", "The correct node was highlighted");

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

@ -0,0 +1,44 @@
/* 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/>. */
// Ignore strange errors when shutting down.
const { PromiseTestUtils } = ChromeUtils.import(
"resource://testing-common/PromiseTestUtils.jsm"
);
PromiseTestUtils.whitelistRejectionsGlobally(/No such actor/);
PromiseTestUtils.whitelistRejectionsGlobally(/connection just closed/);
const MaxItems = 10;
function findNode(dbg, text) {
for (let index = 0; index < MaxItems; index++) {
var elem = findElement(dbg, "scopeNode", index);
if (elem && elem.innerText == text) {
return elem;
}
}
return null;
}
async function toggleNode(dbg, text) {
const node = await waitUntilPredicate(() => findNode(dbg, text));
return toggleObjectInspectorNode(node);
}
// Test that expanded scopes stay expanded after resuming and pausing again.
add_task(async function() {
const dbg = await initDebugger("doc-remember-expanded-scopes.html");
invokeInTab("main", "doc-remember-expanded-scopes.html");
await waitForPaused(dbg);
const MaxItems = 10;
await toggleNode(dbg, "object");
await toggleNode(dbg, "innerObject");
await stepOver(dbg);
await waitForPaused(dbg);
await waitUntil(() => findNode(dbg, "innerData"));
ok("Inner object data automatically expanded after stepping");
});

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

@ -0,0 +1,7 @@
<script>
function main() {
var object = { a:0, b:1, innerObject: { innerData: 0 } };
debugger;
return 0;
}
</script>

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

@ -161,7 +161,7 @@ Tools.inspector = {
preventClosingOnKey: true,
onkey: function(panel, toolbox) {
toolbox.inspector.nodePicker.togglePicker();
toolbox.inspectorFront.nodePicker.togglePicker();
},
isTargetSupported: function(target) {
@ -617,7 +617,7 @@ function createHighlightButton(highlighterName, id) {
isTargetSupported: target => !target.chrome,
async onClick(event, toolbox) {
await toolbox.initInspector();
const highlighter = await toolbox.inspector.getOrCreateHighlighterByType(
const highlighter = await toolbox.inspectorFront.getOrCreateHighlighterByType(
highlighterName
);
if (highlighter.isShown()) {

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

@ -104,12 +104,12 @@ add_task(async function() {
async function inspectorShouldBeOpenAndHighlighting(inspector) {
is(toolbox.currentToolId, "inspector", "Correct tool has been loaded");
await toolbox.inspector.nodePicker.once("picker-started");
await toolbox.inspectorFront.nodePicker.once("picker-started");
ok(true, "picker-started event received, highlighter started");
inspector.synthesizeKey();
await toolbox.inspector.nodePicker.once("picker-stopped");
await toolbox.inspectorFront.nodePicker.once("picker-stopped");
ok(true, "picker-stopped event received, highlighter stopped");
}

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

@ -496,7 +496,7 @@ Toolbox.prototype = {
* Get the toolbox's inspector front. Note that it may not always have been
* initialized first. Use `initInspector()` if needed.
*/
get inspector() {
get inspectorFront() {
return this._inspector;
},
@ -1720,10 +1720,10 @@ Toolbox.prototype = {
if (currentPanel.togglePicker) {
currentPanel.togglePicker(focus);
} else {
if (!this.inspector) {
if (!this.inspectorFront) {
await this.initInspector();
}
this.inspector.nodePicker.togglePicker(focus);
this.inspectorFront.nodePicker.togglePicker(focus);
}
},
@ -1737,7 +1737,7 @@ Toolbox.prototype = {
if (currentPanel.cancelPicker) {
currentPanel.cancelPicker();
} else {
this.inspector.nodePicker.cancel();
this.inspectorFront.nodePicker.cancel();
}
// Stop the console from toggling.
event.stopImmediatePropagation();
@ -1748,7 +1748,7 @@ Toolbox.prototype = {
this.tellRDMAboutPickerState(true);
this.pickerButton.isChecked = true;
await this.selectTool("inspector", "inspect_dom");
this.on("select", this.inspector.nodePicker.stop);
this.on("select", this.inspectorFront.nodePicker.stop);
},
_onPickerStarted: async function() {
@ -1758,7 +1758,7 @@ Toolbox.prototype = {
_onPickerStopped: function() {
this.tellRDMAboutPickerState(false);
this.off("select", this.inspector.nodePicker.stop);
this.off("select", this.inspectorFront.nodePicker.stop);
this.doc.removeEventListener("keypress", this._onPickerKeypress, true);
this.pickerButton.isChecked = false;
},
@ -1881,7 +1881,7 @@ Toolbox.prototype = {
* Update the buttons.
*/
updateToolboxButtons() {
const inspector = this.inspector;
const inspector = this.inspectorFront;
// two of the buttons have highlighters that need to be cleared
// on will-navigate, otherwise we hold on to the stale highlighter
const hasHighlighters =
@ -3267,14 +3267,23 @@ Toolbox.prototype = {
// TODO: replace with getFront once inspector is separated from the toolbox
// TODO: remove these bindings
this._inspector = await this.target.getInspector();
this._walker = this.inspector.walker;
this._highlighter = this.inspector.highlighter;
this._selection = this.inspector.selection;
this._walker = this.inspectorFront.walker;
this._highlighter = this.inspectorFront.highlighter;
this._selection = this.inspectorFront.selection;
this.inspector.nodePicker.on("picker-starting", this._onPickerStarting);
this.inspector.nodePicker.on("picker-started", this._onPickerStarted);
this.inspector.nodePicker.on("picker-stopped", this._onPickerStopped);
this.inspector.nodePicker.on(
this.inspectorFront.nodePicker.on(
"picker-starting",
this._onPickerStarting
);
this.inspectorFront.nodePicker.on(
"picker-started",
this._onPickerStarted
);
this.inspectorFront.nodePicker.on(
"picker-stopped",
this._onPickerStopped
);
this.inspectorFront.nodePicker.on(
"picker-node-canceled",
this._onPickerCanceled
);

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

@ -150,11 +150,11 @@ class AnimationInspector {
this.provider = provider;
this.inspector.sidebar.on("select", this.onSidebarSelectionChanged);
this.inspector.inspector.nodePicker.on(
this.inspector.inspectorFront.nodePicker.on(
"picker-started",
this.onElementPickerStarted
);
this.inspector.inspector.nodePicker.on(
this.inspector.inspectorFront.nodePicker.on(
"picker-stopped",
this.onElementPickerStopped
);
@ -183,11 +183,11 @@ class AnimationInspector {
"inspector-sidebar-resized",
this.onSidebarResized
);
this.inspector.inspector.nodePicker.off(
this.inspector.inspectorFront.nodePicker.off(
"picker-started",
this.onElementPickerStarted
);
this.inspector.inspector.nodePicker.off(
this.inspector.inspectorFront.nodePicker.off(
"picker-stopped",
this.onElementPickerStopped
);
@ -711,7 +711,7 @@ class AnimationInspector {
}
toggleElementPicker() {
this.inspector.inspector.nodePicker.togglePicker();
this.inspector.inspectorFront.nodePicker.togglePicker();
}
async update() {

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

@ -852,7 +852,7 @@ class FontInspector {
async onToggleFontHighlight(font, show, isForCurrentElement = true) {
if (!this.fontsHighlighter) {
try {
this.fontsHighlighter = await this.inspector.inspector.getHighlighterByType(
this.fontsHighlighter = await this.inspector.inspectorFront.getHighlighterByType(
"FontsHighlighter"
);
} catch (e) {

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

@ -227,8 +227,8 @@ Inspector.prototype = {
return this._toolbox;
},
get inspector() {
return this.toolbox.inspector;
get inspectorFront() {
return this.toolbox.inspectorFront;
},
get walker() {
@ -417,7 +417,7 @@ Inspector.prototype = {
},
_getPageStyle: function() {
return this.inspector.getPageStyle().then(pageStyle => {
return this.inspectorFront.getPageStyle().then(pageStyle => {
this.pageStyle = pageStyle;
}, this._handleRejectionIfNotDestroyed);
},
@ -1208,7 +1208,7 @@ Inspector.prototype = {
*/
async supportsEyeDropper() {
try {
return await this.inspector.supportsHighlighters();
return await this.inspectorFront.supportsHighlighters();
} catch (e) {
console.error(e);
return false;
@ -1706,14 +1706,14 @@ Inspector.prototype = {
},
startEyeDropperListeners: function() {
this.inspector.once("color-pick-canceled", this.onEyeDropperDone);
this.inspector.once("color-picked", this.onEyeDropperDone);
this.inspectorFront.once("color-pick-canceled", this.onEyeDropperDone);
this.inspectorFront.once("color-picked", this.onEyeDropperDone);
this.walker.once("new-root", this.onEyeDropperDone);
},
stopEyeDropperListeners: function() {
this.inspector.off("color-pick-canceled", this.onEyeDropperDone);
this.inspector.off("color-picked", this.onEyeDropperDone);
this.inspectorFront.off("color-pick-canceled", this.onEyeDropperDone);
this.inspectorFront.off("color-picked", this.onEyeDropperDone);
this.walker.off("new-root", this.onEyeDropperDone);
},
@ -1736,7 +1736,7 @@ Inspector.prototype = {
this.telemetry.scalarSet(TELEMETRY_EYEDROPPER_OPENED, 1);
this.eyeDropperButton.classList.add("checked");
this.startEyeDropperListeners();
return this.inspector
return this.inspectorFront
.pickColorFromPage({ copyOnSelect: true })
.catch(console.error);
},
@ -1753,7 +1753,7 @@ Inspector.prototype = {
this.eyeDropperButton.classList.remove("checked");
this.stopEyeDropperListeners();
return this.inspector.cancelPickColorFromPage().catch(console.error);
return this.inspectorFront.cancelPickColorFromPage().catch(console.error);
},
/**

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

@ -79,7 +79,7 @@ class MarkupContextMenu {
* This method is here for the benefit of copying links.
*/
_copyAttributeLink(link) {
this.inspector.inspector
this.inspector.inspectorFront
.resolveRelativeURL(link, this.selection.nodeFront)
.then(url => {
clipboardHelper.copyString(url);

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

@ -183,11 +183,11 @@ function MarkupView(inspector, frame, controllerWindow) {
this.walker.on("mutations", this._mutationObserver);
this.win.addEventListener("copy", this._onCopy);
this.win.addEventListener("mouseup", this._onMouseUp);
this.inspector.inspector.nodePicker.on(
this.inspector.inspectorFront.nodePicker.on(
"picker-node-canceled",
this._onToolboxPickerCanceled
);
this.inspector.inspector.nodePicker.on(
this.inspector.inspectorFront.nodePicker.on(
"picker-node-hovered",
this._onToolboxPickerHover
);
@ -932,7 +932,7 @@ MarkupView.prototype = {
if (type === "uri" || type === "cssresource" || type === "jsresource") {
// Open link in a new tab.
this.inspector.inspector
this.inspector.inspectorFront
.resolveRelativeURL(link, this.inspector.selection.nodeFront)
.then(url => {
if (type === "uri") {
@ -2245,7 +2245,7 @@ MarkupView.prototype = {
this._elt.removeEventListener("mouseout", this._onMouseOut);
this._frame.removeEventListener("focus", this._onFocus);
this.inspector.selection.off("new-node-front", this._onNewSelection);
this.inspector.inspector.nodePicker.off(
this.inspector.inspectorFront.nodePicker.off(
"picker-node-hovered",
this._onToolboxPickerHover
);

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

@ -140,7 +140,7 @@ add_task(async function() {
info("Get link from node attribute");
const link = await nodeFront.getAttribute(test.attributeName);
info("Resolve link to absolue URL");
const expected = await inspector.inspector.resolveRelativeURL(
const expected = await inspector.inspectorFront.resolveRelativeURL(
link,
nodeFront
);

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

@ -328,7 +328,7 @@ class RulesView {
}
try {
const front = this.inspector.inspector;
const front = this.inspector.inspectorFront;
this._selectorHighlighter = await front.getHighlighterByType(
"SelectorHighlighter"
);

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

@ -322,7 +322,7 @@ CssRuleView.prototype = {
}
try {
const front = this.inspector.inspector;
const front = this.inspector.inspectorFront;
const h = await front.getHighlighterByType("SelectorHighlighter");
this.selectorHighlighter = h;
return h;

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

@ -12,7 +12,9 @@ add_task(async function() {
await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
const { view, toolbox } = await openRuleView();
const pickerStopped = toolbox.inspector.nodePicker.once("picker-stopped");
const pickerStopped = toolbox.inspectorFront.nodePicker.once(
"picker-stopped"
);
await startPicker(toolbox);

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

@ -95,7 +95,7 @@ async function runTest(testActor, inspector, view) {
async function testESC(swatch, inspector, testActor) {
info("Press escape");
const onCanceled = new Promise(resolve => {
inspector.inspector.once("color-pick-canceled", resolve);
inspector.inspectorFront.once("color-pick-canceled", resolve);
});
await testActor.synthesizeKey({ key: "VK_ESCAPE", options: {} });
await onCanceled;
@ -107,7 +107,7 @@ async function testESC(swatch, inspector, testActor) {
async function testSelect(view, swatch, inspector, testActor) {
info("Click at x:10px y:10px");
const onPicked = new Promise(resolve => {
inspector.inspector.once("color-picked", resolve);
inspector.inspectorFront.once("color-picked", resolve);
});
// The change to the content is done async after rule view change
const onRuleViewChanged = view.once("ruleview-changed");

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

@ -34,7 +34,7 @@ class HighlightersOverlay {
*/
constructor(inspector) {
this.inspector = inspector;
this.inspectorFront = this.inspector.inspector;
this.inspectorFront = this.inspector.inspectorFront;
this.store = this.inspector.store;
this.target = this.inspector.target;
this.telemetry = this.inspector.telemetry;

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

@ -414,7 +414,7 @@ StyleInspectorMenu.prototype = {
let message;
try {
const inspectorFront = this.inspector.inspector;
const inspectorFront = this.inspector.inspectorFront;
const imageUrl = this._clickedNodeInfo.value.url;
const data = await inspectorFront.getImageDataFromURL(imageUrl);
message = await data.data.string();

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

@ -422,7 +422,7 @@ TooltipsOverlay.prototype = {
naturalWidth = size.naturalWidth;
naturalHeight = size.naturalHeight;
} else {
const inspectorFront = this.view.inspector.inspector;
const inspectorFront = this.view.inspector.inspectorFront;
const { data, size } = await inspectorFront.getImageDataFromURL(
imageUrl,
maxDim

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

@ -66,7 +66,7 @@ add_task(async function() {
await testActor.isNodeCorrectlyHighlighted(iframeBodySelector, is);
info("Waiting for the element picker to deactivate.");
await inspector.inspector.nodePicker.stop();
await inspector.inspectorFront.nodePicker.stop();
function moveMouseOver(selector, x, y) {
info("Waiting for element " + selector + " to be highlighted");
@ -76,6 +76,6 @@ add_task(async function() {
y,
options: { type: "mousemove" },
});
return inspector.inspector.nodePicker.once("picker-node-hovered");
return inspector.inspectorFront.nodePicker.once("picker-node-hovered");
}
});

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

@ -95,6 +95,6 @@ add_task(async function() {
y,
options: { type: "mousemove" },
});
return inspector.inspector.nodePicker.once("picker-node-hovered");
return inspector.inspectorFront.nodePicker.once("picker-node-hovered");
}
});

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

@ -18,23 +18,27 @@ add_task(async function() {
await unknownHighlighterTypeShouldntBeAccepted(inspector);
});
async function onlyOneInstanceOfMainHighlighter({ inspector }) {
async function onlyOneInstanceOfMainHighlighter({ inspectorFront }) {
info("Check that the inspector always sends back the same main highlighter");
const h1 = await inspector.getHighlighter(false);
const h2 = await inspector.getHighlighter(false);
const h1 = await inspectorFront.getHighlighter(false);
const h2 = await inspectorFront.getHighlighter(false);
is(h1, h2, "The same highlighter front was returned");
is(h1.typeName, "highlighter", "The right front type was returned");
}
async function manyInstancesOfCustomHighlighters({ inspector }) {
const h1 = await inspector.getHighlighterByType("BoxModelHighlighter");
const h2 = await inspector.getHighlighterByType("BoxModelHighlighter");
async function manyInstancesOfCustomHighlighters({ inspectorFront }) {
const h1 = await inspectorFront.getHighlighterByType("BoxModelHighlighter");
const h2 = await inspectorFront.getHighlighterByType("BoxModelHighlighter");
ok(h1 !== h2, "getHighlighterByType returns new instances every time (1)");
const h3 = await inspector.getHighlighterByType("CssTransformHighlighter");
const h4 = await inspector.getHighlighterByType("CssTransformHighlighter");
const h3 = await inspectorFront.getHighlighterByType(
"CssTransformHighlighter"
);
const h4 = await inspectorFront.getHighlighterByType(
"CssTransformHighlighter"
);
ok(h3 !== h4, "getHighlighterByType returns new instances every time (2)");
ok(
h3 !== h1 && h3 !== h2,
@ -51,9 +55,11 @@ async function manyInstancesOfCustomHighlighters({ inspector }) {
await h4.finalize();
}
async function showHideMethodsAreAvailable({ inspector }) {
const h1 = await inspector.getHighlighterByType("BoxModelHighlighter");
const h2 = await inspector.getHighlighterByType("CssTransformHighlighter");
async function showHideMethodsAreAvailable({ inspectorFront }) {
const h1 = await inspectorFront.getHighlighterByType("BoxModelHighlighter");
const h2 = await inspectorFront.getHighlighterByType(
"CssTransformHighlighter"
);
ok("show" in h1, "Show method is present on the front API");
ok("show" in h2, "Show method is present on the front API");
@ -64,7 +70,7 @@ async function showHideMethodsAreAvailable({ inspector }) {
await h2.finalize();
}
async function unknownHighlighterTypeShouldntBeAccepted({ inspector }) {
const h = await inspector.getHighlighterByType("whatever");
async function unknownHighlighterTypeShouldntBeAccepted({ inspectorFront }) {
const h = await inspectorFront.getHighlighterByType("whatever");
ok(!h, "No highlighter was returned for the invalid type");
}

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

@ -39,7 +39,7 @@ add_task(async function() {
function cancelPickerByShortcut() {
info("Key pressed. Waiting for picker to be canceled.");
testActor.synthesizeKey({ key: "VK_ESCAPE", options: {} });
return inspector.inspector.nodePicker.once("picker-node-canceled");
return inspector.inspectorFront.nodePicker.once("picker-node-canceled");
}
function moveMouseOver(selector) {

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

@ -42,7 +42,7 @@ add_task(async function() {
const { inspector, testActor } = await openInspectorForURL(
"data:text/html;charset=utf-8," + encodeURIComponent(TEST_URL)
);
const front = inspector.inspector;
const front = inspector.inspectorFront;
const highlighter = await front.getHighlighterByType(HIGHLIGHTER_TYPE);
await isHiddenByDefault(testActor, highlighter);

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

@ -23,7 +23,7 @@ add_task(async function() {
const { inspector, testActor } = await openInspectorForURL(
"data:text/html;charset=utf-8," + encodeURIComponent(TEST_URL)
);
const front = inspector.inspector;
const front = inspector.inspectorFront;
const highlighter = await front.getHighlighterByType(HIGHLIGHTER_TYPE);
info("Try to show the highlighter on the grid container");

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

@ -30,7 +30,7 @@ const SHAPE_TYPES = [
add_task(async function() {
const { inspector, testActor } = await openInspectorForURL(TEST_URL);
const front = inspector.inspector;
const front = inspector.inspectorFront;
const highlighter = await front.getHighlighterByType(HIGHLIGHTER_TYPE);
await isHiddenByDefault(testActor, highlighter);

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

@ -11,7 +11,7 @@ const HIGHLIGHTER_TYPE = "ShapesHighlighter";
add_task(async function() {
const { inspector, testActor } = await openInspectorForURL(TEST_URL);
const front = inspector.inspector;
const front = inspector.inspectorFront;
const highlighter = await front.getHighlighterByType(HIGHLIGHTER_TYPE);
await polygonHasCorrectAttrs(testActor, inspector, highlighter);

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

@ -23,7 +23,7 @@ add_task(async function() {
const { inspector, testActor } = await openInspectorForURL(
"data:text/html;charset=utf-8," + encodeURI(TEST_URL)
);
const front = inspector.inspector;
const front = inspector.inspectorFront;
const highlighter = await front.getHighlighterByType(
"CssTransformHighlighter"

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

@ -22,7 +22,7 @@ const TEST_URL = URL_ROOT + "doc_inspector_highlighter_csstransform.html";
add_task(async function() {
const { inspector, testActor } = await openInspectorForURL(TEST_URL);
const front = inspector.inspector;
const front = inspector.inspectorFront;
const highlighter = await front.getHighlighterByType(
"CssTransformHighlighter"

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

@ -70,7 +70,7 @@ add_task(async function() {
);
info("Waiting for element picker to deactivate.");
await inspector.inspector.nodePicker.stop();
await inspector.inspectorFront.nodePicker.stop();
function moveMouseOver(selector) {
info("Waiting for element " + selector + " to be highlighted");
@ -80,6 +80,8 @@ add_task(async function() {
options: { type: "mousemove" },
center: true,
})
.then(() => inspector.inspector.nodePicker.once("picker-node-hovered"));
.then(() =>
inspector.inspectorFront.nodePicker.once("picker-node-hovered")
);
}
});

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

@ -54,12 +54,12 @@ add_task(async function() {
info("First child selection test Passed.");
info("Stopping the picker");
await toolbox.inspector.nodePicker.stop();
await toolbox.inspectorFront.nodePicker.stop();
function doKeyHover(args) {
info("Key pressed. Waiting for element to be highlighted/hovered");
testActor.synthesizeKey(args);
return toolbox.inspector.nodePicker.once("picker-node-hovered");
return toolbox.inspectorFront.nodePicker.once("picker-node-hovered");
}
function moveMouseOver(selector) {
@ -69,6 +69,6 @@ add_task(async function() {
center: true,
selector: selector,
});
return toolbox.inspector.nodePicker.once("picker-node-hovered");
return toolbox.inspectorFront.nodePicker.once("picker-node-hovered");
}
});

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

@ -48,12 +48,12 @@ add_task(async function() {
info("Previously chosen child is remembered. Passed.");
info("Stopping the picker");
await toolbox.inspector.nodePicker.stop();
await toolbox.inspectorFront.nodePicker.stop();
function doKeyHover(args) {
info("Key pressed. Waiting for element to be highlighted/hovered");
const onHighlighterReady = toolbox.once("highlighter-ready");
const onPickerNodeHovered = toolbox.inspector.nodePicker.once(
const onPickerNodeHovered = toolbox.inspectorFront.nodePicker.once(
"picker-node-hovered"
);
testActor.synthesizeKey(args);
@ -63,7 +63,7 @@ add_task(async function() {
function moveMouseOver(selector) {
info("Waiting for element " + selector + " to be highlighted");
const onHighlighterReady = toolbox.once("highlighter-ready");
const onPickerNodeHovered = toolbox.inspector.nodePicker.once(
const onPickerNodeHovered = toolbox.inspectorFront.nodePicker.once(
"picker-node-hovered"
);
testActor.synthesizeMouse({

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

@ -57,20 +57,20 @@ add_task(async function() {
return promise.all([
inspector.selection.once("new-node-front"),
inspector.once("inspector-updated"),
inspector.inspector.nodePicker.once("picker-stopped"),
inspector.inspectorFront.nodePicker.once("picker-stopped"),
]);
}
function doKeyStop(args) {
info("Key pressed. Waiting for picker to be canceled");
testActor.synthesizeKey(args);
return inspector.inspector.nodePicker.once("picker-stopped");
return inspector.inspectorFront.nodePicker.once("picker-stopped");
}
function moveMouseOver(selector) {
info("Waiting for element " + selector + " to be highlighted");
const onHighlighterReady = toolbox.once("highlighter-ready");
const onPickerNodeHovered = inspector.inspector.nodePicker.once(
const onPickerNodeHovered = inspector.inspectorFront.nodePicker.once(
"picker-node-hovered"
);
testActor.synthesizeMouse({

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

@ -15,7 +15,7 @@ add_task(async function() {
await startPicker(toolbox);
info("Start using the picker by hovering over nodes");
const onHover = toolbox.inspector.nodePicker.once("picker-node-hovered");
const onHover = toolbox.inspectorFront.nodePicker.once("picker-node-hovered");
testActor.synthesizeMouse({
options: { type: "mousemove" },
center: true,
@ -24,7 +24,7 @@ add_task(async function() {
await onHover;
info("Press escape and wait for the picker to stop");
const onPickerStopped = toolbox.inspector.nodePicker.once(
const onPickerStopped = toolbox.inspectorFront.nodePicker.once(
"picker-node-canceled"
);
testActor.synthesizeKey({

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

@ -23,7 +23,7 @@ const RULERS_TEXT_STEP = 100;
add_task(async function() {
const { inspector, testActor } = await openInspectorForURL(TEST_URL);
const front = inspector.inspector;
const front = inspector.inspectorFront;
const highlighter = await front.getHighlighterByType("RulersHighlighter");

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

@ -15,7 +15,7 @@ const ID = "rulers-highlighter-";
add_task(async function() {
const { inspector, testActor } = await openInspectorForURL(TEST_URL);
const front = inspector.inspector;
const front = inspector.inspectorFront;
const highlighter = await front.getHighlighterByType("RulersHighlighter");

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

@ -17,7 +17,7 @@ var { Toolbox } = require("devtools/client/framework/toolbox");
add_task(async function() {
const { inspector, testActor } = await openInspectorForURL(TEST_URL);
const front = inspector.inspector;
const front = inspector.inspectorFront;
const highlighter = await front.getHighlighterByType("RulersHighlighter");

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

@ -49,7 +49,7 @@ requestLongerTimeout(5);
add_task(async function() {
const { inspector, testActor } = await openInspectorForURL(TEST_URL);
const front = inspector.inspector;
const front = inspector.inspectorFront;
const highlighter = await front.getHighlighterByType("SelectorHighlighter");
const contextNode = await getNodeFront("body", inspector);

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

@ -42,7 +42,7 @@ requestLongerTimeout(5);
add_task(async function() {
const { inspector, testActor } = await openInspectorForURL(TEST_URL);
const front = inspector.inspector;
const front = inspector.inspectorFront;
const highlighter = await front.getHighlighterByType("SelectorHighlighter");
for (const { inIframe, selector, containerCount } of TEST_DATA) {

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

@ -37,6 +37,6 @@ add_task(async function() {
center: true,
selector: selector,
});
return inspector.inspector.nodePicker.once("picker-node-hovered");
return inspector.inspectorFront.nodePicker.once("picker-node-hovered");
}
});

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

@ -40,5 +40,5 @@ add_task(async function() {
ok(isVisible, "Inspector is highlighting after iframe nav.");
info("Stopping element picker.");
await toolbox.inspector.nodePicker.stop();
await toolbox.inspectorFront.nodePicker.stop();
});

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

@ -13,7 +13,9 @@ const TEST_URI =
add_task(async function() {
const { toolbox } = await openInspectorForURL(TEST_URI);
const pickerStopped = toolbox.inspector.nodePicker.once("picker-stopped");
const pickerStopped = toolbox.inspectorFront.nodePicker.once(
"picker-stopped"
);
info("Starting the inspector picker");
await startPicker(toolbox);

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

@ -67,7 +67,7 @@ add_task(async function() {
await startPickerAndAssertSwitchToInspector(toolbox);
info("Stoppping element picker.");
await toolbox.inspector.nodePicker.stop();
await toolbox.inspectorFront.nodePicker.stop();
checkResults();
});

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

@ -76,7 +76,7 @@ var navigateTo = async function(inspector, url) {
var startPicker = async function(toolbox, skipFocus) {
info("Start the element picker");
toolbox.win.focus();
await toolbox.inspector.nodePicker.start();
await toolbox.inspectorFront.nodePicker.start();
if (!skipFocus) {
// By default make sure the content window is focused since the picker may not focus
// the content window by default.
@ -129,7 +129,9 @@ function pickElement(inspector, testActor, selector, x, y) {
*/
function hoverElement(inspector, testActor, selector, x, y) {
info("Waiting for element " + selector + " to be hovered");
const onHovered = inspector.inspector.nodePicker.once("picker-node-hovered");
const onHovered = inspector.inspectorFront.nodePicker.once(
"picker-node-hovered"
);
testActor.synthesizeMouse({ selector, x, y, options: { type: "mousemove" } });
return onHovered;
}
@ -504,7 +506,7 @@ async function poll(check, desc, attempts = 10, timeBetweenAttempts = 200) {
*/
const getHighlighterHelperFor = type =>
async function({ inspector, testActor }) {
const front = inspector.inspector;
const front = inspector.inspectorFront;
const highlighter = await front.getHighlighterByType(type);
let prefix = "";

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

@ -588,7 +588,7 @@ class Tree extends Component {
super(props);
this.state = {
seen: new Set()
autoExpanded: new Set()
};
this.treeRef = _react2.default.createRef();
@ -632,26 +632,36 @@ class Tree extends Component {
}
_autoExpand() {
const { autoExpandDepth, autoExpandNodeChildrenLimit } = this.props;
if (!autoExpandDepth) {
const {
autoExpandDepth,
autoExpandNodeChildrenLimit,
initiallyExpanded
} = this.props;
if (!autoExpandDepth && !initiallyExpanded) {
return;
}
// Automatically expand the first autoExpandDepth levels for new items. Do
// not use the usual DFS infrastructure because we don't want to ignore
// collapsed nodes.
// collapsed nodes. Any initially expanded items will be expanded regardless
// of how deep they are.
const autoExpand = (item, currentDepth) => {
if (currentDepth >= autoExpandDepth || this.state.seen.has(item)) {
const initial = initiallyExpanded && initiallyExpanded(item);
if (!initial && currentDepth >= autoExpandDepth) {
return;
}
const children = this.props.getChildren(item);
if (autoExpandNodeChildrenLimit && children.length > autoExpandNodeChildrenLimit) {
if (!initial && autoExpandNodeChildrenLimit && children.length > autoExpandNodeChildrenLimit) {
return;
}
this.props.onExpand(item);
this.state.seen.add(item);
if (!this.state.autoExpanded.has(item)) {
this.props.onExpand(item);
this.state.autoExpanded.add(item);
}
const length = children.length;
for (let i = 0; i < length; i++) {
@ -667,6 +677,14 @@ class Tree extends Component {
}
} else if (length != 0) {
autoExpand(roots[0], 0);
if (initiallyExpanded) {
for (let i = 1; i < length; i++) {
if (initiallyExpanded(roots[i])) {
autoExpand(roots[i], 0);
}
}
}
}
}
@ -7001,6 +7019,7 @@ class ObjectInspector extends Component {
nodeExpand,
nodeCollapse,
recordTelemetryEvent,
setExpanded,
roots
} = this.props;
@ -7013,6 +7032,10 @@ class ObjectInspector extends Component {
} else {
nodeCollapse(item);
}
if (setExpanded) {
setExpanded(item, expand);
}
}
focusItem(item) {
@ -7052,6 +7075,7 @@ class ObjectInspector extends Component {
const {
autoExpandAll = true,
autoExpandDepth = 1,
initiallyExpanded,
focusable = true,
disableWrap = false,
expandedPaths,
@ -7067,6 +7091,7 @@ class ObjectInspector extends Component {
autoExpandAll,
autoExpandDepth,
initiallyExpanded,
isExpanded: item => expandedPaths && expandedPaths.has(item.path),
isExpandable: this.isNodeExpandable,
@ -7212,8 +7237,7 @@ function rootsChanged(props) {
function releaseActors(state, client) {
const actors = getActors(state);
for (const actor of actors) {
// Ignore release failure, since the object actor may have been already GC.
client.releaseActor(actor).catch(() => {});
client.releaseActor(actor);
}
}
@ -7586,4 +7610,4 @@ var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*!
/***/ })
/******/ });
});
});

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

@ -236,34 +236,34 @@ class SwatchColorPickerTooltip extends SwatchBasedEditorTooltip {
}
_openEyeDropper() {
const { inspector, toolbox, telemetry } = this.inspector;
const { inspectorFront, toolbox, telemetry } = this.inspector;
telemetry
.getHistogramById(TELEMETRY_PICKER_EYEDROPPER_OPEN_COUNT)
.add(true);
// cancelling picker(if it is already selected) on opening eye-dropper
inspector.nodePicker.cancel();
inspectorFront.nodePicker.cancel();
// pickColorFromPage will focus the content document. If the devtools are in a
// separate window, the colorpicker tooltip will be closed before pickColorFromPage
// resolves. Flip the flag early to avoid issues with onTooltipHidden().
this.eyedropperOpen = true;
inspector.pickColorFromPage({ copyOnSelect: false }).then(() => {
inspectorFront.pickColorFromPage({ copyOnSelect: false }).then(() => {
// close the colorpicker tooltip so that only the eyedropper is open.
this.hide();
this.tooltip.emit("eyedropper-opened");
}, console.error);
inspector.once("color-picked", color => {
inspectorFront.once("color-picked", color => {
toolbox.win.focus();
this._selectColor(color);
this._onEyeDropperDone();
});
inspector.once("color-pick-canceled", () => {
inspectorFront.once("color-pick-canceled", () => {
this._onEyeDropperDone();
});
}

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

@ -158,7 +158,7 @@ StyleEditorUI.prototype = {
this._walker = this._toolbox.walker;
try {
this._highlighter = await this._toolbox.inspector.getHighlighterByType(
this._highlighter = await this._toolbox.inspectorFront.getHighlighterByType(
SELECTOR_HIGHLIGHTER_TYPE
);
} catch (e) {

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

@ -1139,7 +1139,7 @@ function isReverseSearchInputFocused(hud) {
*/
async function selectNodeWithPicker(toolbox, testActor, selector) {
const inspector = toolbox.getPanel("inspector");
const inspectorFront = inspector.inspector;
const inspectorFront = inspector.inspectorFront;
const onPickerStarted = inspectorFront.nodePicker.once("picker-started");
inspectorFront.nodePicker.start();

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

@ -28,7 +28,7 @@ add_task(async function() {
info("Waiting for element picker to become active.");
toolbox.win.focus();
await toolbox.inspector.nodePicker.start();
await toolbox.inspectorFront.nodePicker.start();
info("Moving mouse over div.");
await moveMouseOver("#maindiv", 1, 1);
@ -50,6 +50,6 @@ add_task(async function() {
y,
options: { type: "mousemove" },
});
return inspector.inspector.nodePicker.once("picker-node-hovered");
return inspector.inspectorFront.nodePicker.once("picker-node-hovered");
}
});

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

@ -111,6 +111,9 @@ function ChildProcess(id, recording) {
// The last point we paused at.
this.lastPausePoint = null;
// Last reported memory usage for this child.
this.lastMemoryUsage = null;
// Manifests which this child needs to send asynchronously.
this.asyncManifests = [];
@ -168,19 +171,25 @@ ChildProcess.prototype = {
this.paused = false;
this.manifest = manifest;
dumpv(`SendManifest #${this.id} ${JSON.stringify(manifest.contents)}`);
dumpv(`SendManifest #${this.id} ${stringify(manifest.contents)}`);
RecordReplayControl.sendManifest(this.id, manifest.contents);
},
// Called when the child's current manifest finishes.
manifestFinished(response) {
assert(!this.paused);
if (response && response.point) {
this.lastPausePoint = response.point;
if (response) {
if (response.point) {
this.lastPausePoint = response.point;
}
if (response.memoryUsage) {
this.lastMemoryUsage = response.memoryUsage;
}
}
this.paused = true;
this.manifest.onFinished(response);
this.manifest = null;
maybeDumpStatistics();
},
// Block until this child is paused. If maybeCreateCheckpoint is specified
@ -346,6 +355,15 @@ function CheckpointInfo() {
// If the checkpoint is saved, the replaying child responsible for saving it
// and scanning the region up to the next saved checkpoint.
this.owner = null;
// If the checkpoint is saved, the time it was assigned an owner.
this.assignTime = null;
// If the checkpoint is saved and scanned, the time it finished being scanned.
this.scanTime = null;
// If the checkpoint is saved and scanned, the duration of the scan.
this.scanDuration = null;
}
function getCheckpointInfo(id) {
@ -364,18 +382,22 @@ function timeSinceCheckpoint(id) {
return time;
}
// How much execution time is captured by a saved checkpoint.
function timeForSavedCheckpoint(id) {
const next = nextSavedCheckpoint(id);
let time = 0;
for (let i = id; i < next; i++) {
time += gCheckpoints[i].duration;
}
return time;
}
// The checkpoint up to which the recording runs.
let gLastFlushCheckpoint = InvalidCheckpointId;
// The last saved checkpoint.
let gLastSavedCheckpoint = FirstCheckpointId;
// How often we want to flush the recording.
const FlushMs = 0.5 * 1000;
// How often we want to save a checkpoint.
const SavedCheckpointMs = 0.25 * 1000;
function addSavedCheckpoint(checkpoint) {
if (getCheckpointInfo(checkpoint).owner) {
return;
@ -383,26 +405,16 @@ function addSavedCheckpoint(checkpoint) {
const owner = pickReplayingChild();
getCheckpointInfo(checkpoint).owner = owner;
getCheckpointInfo(checkpoint).assignTime = Date.now();
owner.addSavedCheckpoint(checkpoint);
gLastSavedCheckpoint = checkpoint;
}
function addCheckpoint(checkpoint, duration) {
assert(!getCheckpointInfo(checkpoint).duration);
getCheckpointInfo(checkpoint).duration = duration;
// Mark saved checkpoints as required, unless we haven't spawned any replaying
// children yet.
if (
timeSinceCheckpoint(gLastSavedCheckpoint) >= SavedCheckpointMs &&
gReplayingChildren.length > 0
) {
addSavedCheckpoint(checkpoint + 1);
}
}
function ownerChild(checkpoint) {
assert(checkpoint <= gLastSavedCheckpoint);
while (!getCheckpointInfo(checkpoint).owner) {
checkpoint--;
}
@ -559,7 +571,14 @@ function scanRecording(checkpoint) {
needSaveCheckpoints: child.flushNeedSaveCheckpoints(),
};
},
onFinished: child => child.scannedCheckpoints.add(checkpoint),
onFinished(child, { duration }) {
child.scannedCheckpoints.add(checkpoint);
const info = getCheckpointInfo(checkpoint);
if (!info.scanTime) {
info.scanTime = Date.now();
info.scanDuration = duration;
}
},
},
checkpointExecutionPoint(checkpoint)
);
@ -1006,8 +1025,8 @@ function handleResumeManifestResponse({
consoleMessages.forEach(msg => gDebugger.onConsoleMessage(msg));
}
if (gDebugger && gDebugger.onNewScript) {
scripts.forEach(script => gDebugger.onNewScript(script));
if (gDebugger) {
scripts.forEach(script => gDebugger._onNewScript(script));
}
}
@ -1071,8 +1090,8 @@ function ensureFlushed() {
spawnReplayingChildren();
}
// Checkpoints where the recording was flushed to disk are always saved.
// This allows the recording to be scanned as soon as it has been flushed.
// Checkpoints where the recording was flushed to disk are saved. This allows
// the recording to be scanned as soon as it has been flushed.
addSavedCheckpoint(gLastFlushCheckpoint);
// Flushing creates a new region of the recording for replaying children
@ -1156,7 +1175,7 @@ function Initialize(recordingChildId) {
// eslint-disable-next-line no-unused-vars
function ManifestFinished(id, response) {
try {
dumpv(`ManifestFinished #${id} ${JSON.stringify(response)}`);
dumpv(`ManifestFinished #${id} ${stringify(response)}`);
lookupChild(id).manifestFinished(response);
} catch (e) {
dump(`ERROR: ManifestFinished threw exception: ${e} ${e.stack}\n`);
@ -1331,6 +1350,75 @@ const gControl = {
},
};
///////////////////////////////////////////////////////////////////////////////
// Statistics
///////////////////////////////////////////////////////////////////////////////
let lastDumpTime = Date.now();
function maybeDumpStatistics() {
const now = Date.now();
if (now - lastDumpTime < 5000) {
return;
}
lastDumpTime = now;
let delayTotal = 0;
let unscannedTotal = 0;
let timeTotal = 0;
let scanDurationTotal = 0;
forSavedCheckpointsInRange(
FirstCheckpointId,
gLastFlushCheckpoint,
checkpoint => {
const checkpointTime = timeForSavedCheckpoint(checkpoint);
const info = getCheckpointInfo(checkpoint);
timeTotal += checkpointTime;
if (info.scanTime) {
delayTotal += checkpointTime * (info.scanTime - info.assignTime);
scanDurationTotal += info.scanDuration;
} else {
unscannedTotal += checkpointTime;
}
}
);
const memoryUsage = [];
let totalSaved = 0;
for (const child of gReplayingChildren) {
if (!child) {
continue;
}
totalSaved += child.savedCheckpoints.size;
if (!child.lastMemoryUsage) {
continue;
}
for (const [name, value] of Object.entries(child.lastMemoryUsage)) {
if (!memoryUsage[name]) {
memoryUsage[name] = 0;
}
memoryUsage[name] += value;
}
}
const delay = delayTotal / timeTotal;
const overhead = scanDurationTotal / (timeTotal - unscannedTotal);
dumpv(`Statistics:`);
dumpv(`Total recording time: ${timeTotal}`);
dumpv(`Unscanned fraction: ${unscannedTotal / timeTotal}`);
dumpv(`Average scan delay: ${delay}`);
dumpv(`Average scanning overhead: ${overhead}`);
dumpv(`Saved checkpoints: ${totalSaved}`);
for (const [name, value] of Object.entries(memoryUsage)) {
dumpv(`Memory ${name}: ${value}`);
}
}
///////////////////////////////////////////////////////////////////////////////
// Utilities
///////////////////////////////////////////////////////////////////////////////
@ -1341,8 +1429,15 @@ function ConnectDebugger(dbg) {
dbg._control = gControl;
}
const startTime = Date.now();
// eslint-disable-next-line no-unused-vars
function currentTime() {
return (((Date.now() - startTime) / 10) | 0) / 100;
}
function dumpv(str) {
//dump(`[ReplayControl] ${str}\n`);
//dump(`[ReplayControl ${currentTime()}] ${str}\n`);
}
function assert(v) {
@ -1357,6 +1452,14 @@ function ThrowError(msg) {
throw error;
}
function stringify(object) {
const str = JSON.stringify(object);
if (str && str.length >= 4096) {
return `${str.substr(0, 4096)} TRIMMED ${str.length}`;
}
return str;
}
// eslint-disable-next-line no-unused-vars
var EXPORTED_SYMBOLS = [
"Initialize",

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

@ -123,9 +123,7 @@ ReplayDebugger.prototype = {
},
_processResponse(request, response, divergeResponse) {
dumpv(
`SendRequest: ${JSON.stringify(request)} -> ${JSON.stringify(response)}`
);
dumpv(`SendRequest: ${stringify(request)} -> ${stringify(response)}`);
if (response.exception) {
ThrowError(response.exception);
}
@ -324,7 +322,7 @@ ReplayDebugger.prototype = {
replayPushThreadPause() {
// The thread has paused so that the user can interact with it. The child
// will stay paused until this thread-wide pause has been popped.
assert(this._paused);
this._ensurePaused();
assert(!this._resumeCallback);
if (++this._threadPauseCount == 1) {
// There is no preferred direction of travel after an explicit pause.
@ -363,7 +361,8 @@ ReplayDebugger.prototype = {
},
_performResume() {
assert(this._paused && !this._threadPauseCount);
this._ensurePaused();
assert(!this._threadPauseCount);
if (this._resumeCallback && !this._threadPauseCount) {
const callback = this._resumeCallback;
this._invalidateAfterUnpause();
@ -528,11 +527,11 @@ ReplayDebugger.prototype = {
return data.map(script => this._addScript(script));
},
findAllConsoleMessages() {
const messages = this._sendRequestMainChild({
type: "findConsoleMessages",
});
return messages.map(this._convertConsoleMessage.bind(this));
_onNewScript(data) {
if (this.onNewScript) {
const script = this._addScript(data);
this.onNewScript(script);
}
},
/////////////////////////////////////////////////////////
@ -544,7 +543,9 @@ ReplayDebugger.prototype = {
if (source) {
return source;
}
return this._addSource(this._sendRequest({ type: "getSource", id }));
return this._addSource(
this._sendRequestMainChild({ type: "getSource", id })
);
},
_addSource(data) {
@ -692,6 +693,13 @@ ReplayDebugger.prototype = {
return message;
},
findAllConsoleMessages() {
const messages = this._sendRequestMainChild({
type: "findConsoleMessages",
});
return messages.map(this._convertConsoleMessage.bind(this));
},
/////////////////////////////////////////////////////////
// Handlers
/////////////////////////////////////////////////////////
@ -1362,4 +1370,12 @@ function isNonNullObject(obj) {
return obj && (typeof obj == "object" || typeof obj == "function");
}
function stringify(object) {
const str = JSON.stringify(object);
if (str.length >= 4096) {
return `${str.substr(0, 4096)} TRIMMED ${str.length}`;
}
return str;
}
module.exports = ReplayDebugger;

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

@ -68,7 +68,9 @@ dbg.onNewGlobalObject = function(global) {
// Utilities
///////////////////////////////////////////////////////////////////////////////
const dump = RecordReplayControl.dump;
const dump = str => {
RecordReplayControl.dump(`[Child #${RecordReplayControl.childId()}]: ${str}`);
};
function assert(v) {
if (!v) {
@ -143,6 +145,24 @@ function isNonNullObject(obj) {
return obj && (typeof obj == "object" || typeof obj == "function");
}
function getMemoryUsage() {
const memoryKinds = {
Generic: [1],
Snapshots: [2, 3, 4, 5, 6, 7],
ScriptHits: [8],
};
const rv = {};
for (const [name, kinds] of Object.entries(memoryKinds)) {
let total = 0;
kinds.forEach(kind => {
total += RecordReplayControl.memoryUsage(kind);
});
rv[name] = total;
}
return rv;
}
///////////////////////////////////////////////////////////////////////////////
// Persistent Script State
///////////////////////////////////////////////////////////////////////////////
@ -637,16 +657,16 @@ function ClearPausedState() {
// The manifest that is currently being processed.
let gManifest;
// When processing "resume" manifests this tracks the execution time when we
// started execution from the initial checkpoint.
let gTimeWhenResuming;
// When processing certain manifests this tracks the execution time when the
// manifest started executing.
let gManifestStartTime;
// Handlers that run when a manifest is first received. This must be specified
// for all manifests.
const gManifestStartHandlers = {
resume({ breakpoints }) {
RecordReplayControl.resumeExecution();
gTimeWhenResuming = RecordReplayControl.currentExecutionTime();
gManifestStartTime = RecordReplayControl.currentExecutionTime();
breakpoints.forEach(ensurePositionHandler);
},
@ -663,6 +683,7 @@ const gManifestStartHandlers = {
},
scanRecording(manifest) {
gManifestStartTime = RecordReplayControl.currentExecutionTime();
gManifestStartHandlers.runToPoint(manifest);
},
@ -801,7 +822,7 @@ const gManifestFinishedAfterCheckpointHandlers = {
resume(_, point) {
RecordReplayControl.manifestFinished({
point,
duration: RecordReplayControl.currentExecutionTime() - gTimeWhenResuming,
duration: RecordReplayControl.currentExecutionTime() - gManifestStartTime,
consoleMessages: gNewConsoleMessages,
scripts: gNewScripts,
});
@ -810,6 +831,7 @@ const gManifestFinishedAfterCheckpointHandlers = {
},
runToPoint({ endpoint }, point) {
assert(endpoint.checkpoint >= point.checkpoint);
if (!endpoint.position && point.checkpoint == endpoint.checkpoint) {
RecordReplayControl.manifestFinished({ point });
}
@ -817,7 +839,13 @@ const gManifestFinishedAfterCheckpointHandlers = {
scanRecording({ endpoint }, point) {
if (point.checkpoint == endpoint) {
RecordReplayControl.manifestFinished({ point });
const duration =
RecordReplayControl.currentExecutionTime() - gManifestStartTime;
RecordReplayControl.manifestFinished({
point,
duration,
memoryUsage: getMemoryUsage(),
});
}
},
};
@ -892,6 +920,7 @@ let gFrameStepsFrameIndex = 0;
// This must be specified for any manifest that uses ensurePositionHandler.
const gManifestPositionHandlers = {
resume(manifest, point) {
clearPositionHandlers();
RecordReplayControl.manifestFinished({
point,
consoleMessages: gNewConsoleMessages,
@ -1066,7 +1095,7 @@ function getObjectData(id) {
optimizedOut: object.optimizedOut,
};
}
throwError("Unknown object kind");
throwError(`Unknown object kind: ${object}`);
}
function getObjectProperties(object) {
@ -1249,6 +1278,7 @@ function getPauseData() {
const names = getEnvironmentNames(env);
rv.environments[id] = { data, names };
addObject(data.callee);
addEnvironment(data.parent);
}

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

@ -1312,5 +1312,12 @@ void RestoreMemoryToLastSavedDiffCheckpoint() {
gMemoryInfo->mSnapshotThreadsShouldRestore.ActivateEnd();
}
size_t GetMemoryUsage(MemoryKind aKind) {
if (!gMemoryInfo) {
return 0;
}
return gMemoryInfo->mMemoryBalance[(size_t)aKind];
}
} // namespace recordreplay
} // namespace mozilla

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

@ -126,6 +126,9 @@ void MemoryMove(void* aDst, const void* aSrc, size_t aSize);
// dynamic code loading.
void MemoryZero(void* aDst, size_t aSize);
// Get the amount of allocated memory used by data of the specified kind.
size_t GetMemoryUsage(MemoryKind aKind);
} // namespace recordreplay
} // namespace mozilla

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

@ -340,10 +340,30 @@ static void MM_CFTypeOutputArg(MiddlemanCallContext& aCx) {
MM_CFTypeOutput(aCx, arg, /* aOwnsReference = */ false);
}
static void SendMessageToObject(const void* aObject, const char* aMessage) {
CallArguments arguments;
arguments.Arg<0, const void*>() = aObject;
arguments.Arg<1, SEL>() = sel_registerName(aMessage);
RecordReplayInvokeCall(gOriginal_objc_msgSend, &arguments);
}
// For APIs whose result will be released by the middleman's autorelease pool.
static void MM_AutoreleaseCFTypeRval(MiddlemanCallContext& aCx) {
auto& rval = aCx.mArguments->Rval<const void*>();
MM_SystemOutput(aCx, &rval);
if (rval) {
switch (aCx.mPhase) {
case MiddlemanCallPhase::MiddlemanOutput:
SendMessageToObject(rval, "retain");
break;
case MiddlemanCallPhase::MiddlemanRelease:
SendMessageToObject(rval, "autorelease");
break;
default:
break;
}
}
}
// For functions which have an input CFType value and also have side effects on

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

@ -345,6 +345,10 @@ void ReportFatalError(const Maybe<MinidumpInfo>& aMinidump, const char* aFormat,
Thread::WaitForeverNoIdle();
}
size_t GetId() {
return gChannel->GetId();
}
///////////////////////////////////////////////////////////////////////////////
// Vsyncs
///////////////////////////////////////////////////////////////////////////////

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

@ -39,6 +39,9 @@ struct MinidumpInfo {
void ReportFatalError(const Maybe<MinidumpInfo>& aMinidumpInfo,
const char* aFormat, ...);
// Get the unique ID of this child.
size_t GetId();
// Monitor used for various synchronization tasks.
extern Monitor* gMonitor;

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

@ -264,13 +264,17 @@ void ChildProcessInfo::WaitUntilPaused() {
bool sentTerminateMessage = false;
while (true) {
MonitorAutoLock lock(*gMonitor);
Maybe<MonitorAutoLock> lock;
lock.emplace(*gMonitor);
MaybeHandlePendingSyncMessage();
// Search for the first message received from this process.
ChildProcessInfo* process = this;
Message::UniquePtr msg = ExtractChildMessage(&process);
if (msg) {
lock.reset();
OnIncomingMessage(*msg);
if (IsPaused()) {
return;

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

@ -565,6 +565,13 @@ static bool FetchContent(JSContext* aCx, HandleString aURL,
// Recording/Replaying Methods
///////////////////////////////////////////////////////////////////////////////
static bool RecordReplay_ChildId(JSContext* aCx, unsigned aArgc, Value* aVp) {
CallArgs args = CallArgsFromVp(aArgc, aVp);
args.rval().setInt32(child::GetId());
return true;
}
static bool RecordReplay_AreThreadEventsDisallowed(JSContext* aCx,
unsigned aArgc, Value* aVp) {
CallArgs args = CallArgsFromVp(aArgc, aVp);
@ -971,6 +978,26 @@ static bool RecordReplay_Repaint(JSContext* aCx, unsigned aArgc, Value* aVp) {
return true;
}
static bool RecordReplay_MemoryUsage(JSContext* aCx, unsigned aArgc,
Value* aVp) {
CallArgs args = CallArgsFromVp(aArgc, aVp);
if (!args.get(0).isNumber()) {
JS_ReportErrorASCII(aCx, "Bad memory kind");
return false;
}
size_t kind = args.get(0).toNumber();
if (kind >= (size_t) MemoryKind::Count) {
JS_ReportErrorASCII(aCx, "Memory kind out of range");
return false;
}
args.rval().setDouble(GetMemoryUsage((MemoryKind) kind));
return true;
}
static bool RecordReplay_Dump(JSContext* aCx, unsigned aArgc, Value* aVp) {
// This method is an alternative to dump() that can be used in places where
// thread events are disallowed.
@ -1007,6 +1034,7 @@ static const JSFunctionSpec gMiddlemanMethods[] = {
JS_FS_END};
static const JSFunctionSpec gRecordReplayMethods[] = {
JS_FN("childId", RecordReplay_ChildId, 0, 0),
JS_FN("areThreadEventsDisallowed", RecordReplay_AreThreadEventsDisallowed,
0, 0),
JS_FN("divergeFromRecording", RecordReplay_DivergeFromRecording, 0, 0),
@ -1025,6 +1053,7 @@ static const JSFunctionSpec gRecordReplayMethods[] = {
JS_FN("findScriptHits", RecordReplay_FindScriptHits, 2, 0),
JS_FN("getContent", RecordReplay_GetContent, 1, 0),
JS_FN("repaint", RecordReplay_Repaint, 0, 0),
JS_FN("memoryUsage", RecordReplay_MemoryUsage, 0, 0),
JS_FN("dump", RecordReplay_Dump, 1, 0),
JS_FS_END};

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

@ -260,89 +260,83 @@ class MiddlemanProtocol : public ipc::IToplevelProtocol {
return MsgProcessed;
}
static void ForwardMessageSync(MiddlemanProtocol* aProtocol,
Message* aMessage, Message** aReply) {
PrintSpew("ForwardSyncMsg %s\n",
IPC::StringFromIPCMessageType(aMessage->type()));
Message* mSyncMessage = nullptr;
Message* mSyncMessageReply = nullptr;
bool mSyncMessageIsCall = false;
MOZ_RELEASE_ASSERT(!*aReply);
Message* nReply = new Message();
if (!aProtocol->GetIPCChannel()->Send(aMessage, nReply)) {
MOZ_RELEASE_ASSERT(aProtocol->mSide == ipc::ParentSide);
void MaybeSendSyncMessage(bool aLockHeld) {
Maybe<MonitorAutoLock> lock;
if (!aLockHeld) {
lock.emplace(*gMonitor);
}
if (!mSyncMessage) {
return;
}
PrintSpew("ForwardSyncMsg %s\n",
IPC::StringFromIPCMessageType(mSyncMessage->type()));
MOZ_RELEASE_ASSERT(!mSyncMessageReply);
mSyncMessageReply = new Message();
if (mSyncMessageIsCall
? !mOpposite->GetIPCChannel()->Call(mSyncMessage, mSyncMessageReply)
: !mOpposite->GetIPCChannel()->Send(mSyncMessage, mSyncMessageReply)) {
MOZ_RELEASE_ASSERT(mSide == ipc::ChildSide);
BeginShutdown();
}
MonitorAutoLock lock(*gMonitor);
*aReply = nReply;
gMonitor->Notify();
mSyncMessage = nullptr;
gMonitor->NotifyAll();
}
static void StaticMaybeSendSyncMessage(MiddlemanProtocol* aProtocol) {
aProtocol->MaybeSendSyncMessage(false);
}
void HandleSyncMessage(const Message& aMessage, Message*& aReply, bool aCall) {
MOZ_RELEASE_ASSERT(mOppositeMessageLoop);
mSyncMessage = new Message();
mSyncMessage->CopyFrom(aMessage);
mSyncMessageIsCall = aCall;
mOppositeMessageLoop->PostTask(
NewRunnableFunction("StaticMaybeSendSyncMessage", StaticMaybeSendSyncMessage, this));
if (mSide == ipc::ChildSide) {
AutoMarkMainThreadWaitingForIPDLReply blocked;
while (!mSyncMessageReply) {
MOZ_CRASH("NYI");
}
} else {
MonitorAutoLock lock(*gMonitor);
// If the main thread is blocked waiting for the recording child to pause,
// wake it up so it can call MaybeHandlePendingSyncMessage().
gMonitor->NotifyAll();
while (!mSyncMessageReply) {
gMonitor->Wait();
}
}
aReply = mSyncMessageReply;
mSyncMessageReply = nullptr;
PrintSpew("SyncMsgDone\n");
}
virtual Result OnMessageReceived(const Message& aMessage,
Message*& aReply) override {
MOZ_RELEASE_ASSERT(mOppositeMessageLoop);
Message* nMessage = new Message();
nMessage->CopyFrom(aMessage);
mOppositeMessageLoop->PostTask(
NewRunnableFunction("ForwardMessageSync", ForwardMessageSync, mOpposite,
nMessage, &aReply));
if (mSide == ipc::ChildSide) {
AutoMarkMainThreadWaitingForIPDLReply blocked;
while (!aReply) {
MOZ_CRASH("NYI");
}
} else {
MonitorAutoLock lock(*gMonitor);
while (!aReply) {
gMonitor->Wait();
}
}
PrintSpew("SyncMsgDone\n");
HandleSyncMessage(aMessage, aReply, false);
return MsgProcessed;
}
static void ForwardCallMessage(MiddlemanProtocol* aProtocol,
Message* aMessage, Message** aReply) {
PrintSpew("ForwardSyncCall %s\n",
IPC::StringFromIPCMessageType(aMessage->type()));
MOZ_RELEASE_ASSERT(!*aReply);
Message* nReply = new Message();
if (!aProtocol->GetIPCChannel()->Call(aMessage, nReply)) {
MOZ_RELEASE_ASSERT(aProtocol->mSide == ipc::ParentSide);
BeginShutdown();
}
MonitorAutoLock lock(*gMonitor);
*aReply = nReply;
gMonitor->Notify();
}
virtual Result OnCallReceived(const Message& aMessage,
Message*& aReply) override {
MOZ_RELEASE_ASSERT(mOppositeMessageLoop);
Message* nMessage = new Message();
nMessage->CopyFrom(aMessage);
mOppositeMessageLoop->PostTask(
NewRunnableFunction("ForwardCallMessage", ForwardCallMessage, mOpposite,
nMessage, &aReply));
if (mSide == ipc::ChildSide) {
AutoMarkMainThreadWaitingForIPDLReply blocked;
while (!aReply) {
MOZ_CRASH("NYI");
}
} else {
MonitorAutoLock lock(*gMonitor);
while (!aReply) {
gMonitor->Wait();
}
}
PrintSpew("SyncCallDone\n");
HandleSyncMessage(aMessage, aReply, true);
return MsgProcessed;
}
@ -357,6 +351,12 @@ class MiddlemanProtocol : public ipc::IToplevelProtocol {
static MiddlemanProtocol* gChildProtocol;
static MiddlemanProtocol* gParentProtocol;
void MaybeHandlePendingSyncMessage() {
if (gParentProtocol) {
gParentProtocol->MaybeSendSyncMessage(true);
}
}
ipc::MessageChannel* ChannelToUIProcess() {
return gChildProtocol->GetIPCChannel();
}

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

@ -51,6 +51,11 @@ bool MainThreadIsWaitingForIPDLReply();
// to block while waiting on an IPDL reply from the child.
void ResumeBeforeWaitingForIPDLReply();
// Immediately forward any sync child->parent IPDL message. These are sent on
// the main thread, which might be blocked waiting for a response from the
// recording child and unable to run an event loop.
void MaybeHandlePendingSyncMessage();
// Initialize state which handles incoming IPDL messages from the UI and
// recording child processes.
void InitializeForwarding();