/* 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/>. */
import {
getSelectedFrame,
getCurrentThread,
getIsCurrentThreadPaused,
getIsPaused,
} from
"../../selectors/index";
import { PROMISE } from
"../utils/middleware/promise";
import { evaluateExpressions } from
"../expressions";
import { selectLocation } from
"../sources/index";
import { fetchScopes } from
"./fetchScopes";
import { fetchFrames } from
"./fetchFrames";
import { recordEvent } from
"../../utils/telemetry";
import { validateFrame } from
"../../utils/context";
export
function selectThread(
thread) {
return async ({ dispatch, getState }) => {
if (getCurrentThread(getState()) ===
thread) {
return;
}
dispatch({ type:
"SELECT_THREAD",
thread });
if (getCurrentThread(getState()) !==
thread) {
throw new Error(
"The thread wasn't selected");
}
const selectedFrame = getSelectedFrame(getState());
const serverRequests = [];
// Update the watched expressions as we may never have evaluated them against this thread
// Note that selectedFrame may be null if the thread isn't paused.
serverRequests.push(dispatch(evaluateExpressions(selectedFrame)));
// If we were paused on the newly selected thread, ensure:
// - select the source where we are paused,
// - fetching the paused stackframes,
// - fetching the paused scope, so that variable preview are working on the selected source.
// (frames and scopes is supposed to be fetched on pause,
// but if two threads pause concurrently, it might be cancelled)
if (selectedFrame) {
serverRequests.push(dispatch(selectLocation(selectedFrame.location)));
serverRequests.push(dispatch(fetchFrames(
thread)));
serverRequests.push(dispatch(fetchScopes()));
}
await Promise.all(serverRequests);
};
}
/**
* Debugger commands like stepOver, stepIn, stepOut, resume
*
* @param string type
*/
export
function command(type) {
return async ({ dispatch, getState, client }) => {
if (!type) {
return null;
}
// For now, all commands are by default against the currently selected thread
const thread = getCurrentThread(getState());
const frame = getSelectedFrame(getState());
return dispatch({
type:
"COMMAND",
command: type,
thread,
[PROMISE]: client[type](
thread, frame?.id),
});
};
}
/**
* StepIn
*
* @returns {Function} {@link command}
*/
export
function stepIn() {
return ({ dispatch, getState }) => {
if (!getIsCurrentThreadPaused(getState())) {
return null;
}
return dispatch(command(
"stepIn"));
};
}
/**
* stepOver
*
* @returns {Function} {@link command}
*/
export
function stepOver() {
return ({ dispatch, getState }) => {
if (!getIsCurrentThreadPaused(getState())) {
return null;
}
return dispatch(command(
"stepOver"));
};
}
/**
* stepOut
*
* @returns {Function} {@link command}
*/
export
function stepOut() {
return ({ dispatch, getState }) => {
if (!getIsCurrentThreadPaused(getState())) {
return null;
}
return dispatch(command(
"stepOut"));
};
}
/**
* resume
*
* @returns {Function} {@link command}
*/
export
function resume() {
return ({ dispatch, getState }) => {
if (!getIsCurrentThreadPaused(getState())) {
return null;
}
recordEvent(
"continue");
return dispatch(command(
"resume"));
};
}
/**
* restart frame
*/
export
function restart(frame) {
return async ({ dispatch, getState, client }) => {
if (!getIsPaused(getState(), frame.
thread)) {
return null;
}
validateFrame(getState(), frame);
return dispatch({
type:
"COMMAND",
command:
"restart",
thread: frame.
thread,
[PROMISE]: client.restart(frame.
thread, frame.id),
});
};
}