зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
Коммит
86980b4bb2
|
@ -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();
|
||||
|
|
Загрузка…
Ссылка в новой задаче