summaryrefslogtreecommitdiffstats
path: root/devtools/client/debugger/src/actions/preview.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/debugger/src/actions/preview.js')
-rw-r--r--devtools/client/debugger/src/actions/preview.js211
1 files changed, 211 insertions, 0 deletions
diff --git a/devtools/client/debugger/src/actions/preview.js b/devtools/client/debugger/src/actions/preview.js
new file mode 100644
index 0000000000..992737e2d1
--- /dev/null
+++ b/devtools/client/debugger/src/actions/preview.js
@@ -0,0 +1,211 @@
+/* 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 { isConsole } from "../utils/preview";
+import { findBestMatchExpression } from "../utils/ast";
+import { getGrip, getFront } from "../utils/evaluation-result";
+import { getExpressionFromCoords } from "../utils/editor/get-expression";
+import { isNodeTest } from "../utils/environment";
+
+import {
+ getPreview,
+ isLineInScope,
+ isSelectedFrameVisible,
+ getSelectedSource,
+ getSelectedLocation,
+ getSelectedFrame,
+ getSymbols,
+ getCurrentThread,
+ getPreviewCount,
+ getSelectedException,
+} from "../selectors";
+
+import { getMappedExpression } from "./expressions";
+
+function findExpressionMatch(state, codeMirror, tokenPos) {
+ const location = getSelectedLocation(state);
+ if (!location) {
+ return null;
+ }
+
+ const symbols = getSymbols(state, location);
+
+ let match;
+ if (!symbols) {
+ match = getExpressionFromCoords(codeMirror, tokenPos);
+ } else {
+ match = findBestMatchExpression(symbols, tokenPos);
+ }
+ return match;
+}
+
+export function updatePreview(cx, target, tokenPos, codeMirror) {
+ return ({ dispatch, getState }) => {
+ const cursorPos = target.getBoundingClientRect();
+
+ if (
+ !isSelectedFrameVisible(getState()) ||
+ !isLineInScope(getState(), tokenPos.line)
+ ) {
+ return;
+ }
+
+ const match = findExpressionMatch(getState(), codeMirror, tokenPos);
+ if (!match) {
+ return;
+ }
+
+ const { expression, location } = match;
+
+ if (isConsole(expression)) {
+ return;
+ }
+
+ dispatch(setPreview(cx, expression, location, tokenPos, cursorPos, target));
+ };
+}
+
+export function setPreview(
+ cx,
+ expression,
+ location,
+ tokenPos,
+ cursorPos,
+ target
+) {
+ return async ({ dispatch, getState, client }) => {
+ dispatch({ type: "START_PREVIEW" });
+ const previewCount = getPreviewCount(getState());
+ if (getPreview(getState())) {
+ dispatch(clearPreview(cx));
+ }
+
+ const source = getSelectedSource(getState());
+ if (!source) {
+ return;
+ }
+
+ const thread = getCurrentThread(getState());
+ const selectedFrame = getSelectedFrame(getState(), thread);
+
+ if (location && source.isOriginal) {
+ const mapResult = await dispatch(getMappedExpression(expression));
+ if (mapResult) {
+ expression = mapResult.expression;
+ }
+ }
+
+ if (!selectedFrame) {
+ return;
+ }
+
+ const { result } = await client.evaluate(expression, {
+ frameId: selectedFrame.id,
+ });
+
+ const resultGrip = getGrip(result);
+
+ // Error case occurs for a token that follows an errored evaluation
+ // https://github.com/firefox-devtools/debugger/pull/8056
+ // Accommodating for null allows us to show preview for falsy values
+ // line "", false, null, Nan, and more
+ if (resultGrip === null) {
+ return;
+ }
+
+ // Handle cases where the result is invisible to the debugger
+ // and not possible to preview. Bug 1548256
+ if (
+ resultGrip &&
+ resultGrip.class &&
+ typeof resultGrip.class === "string" &&
+ resultGrip.class.includes("InvisibleToDebugger")
+ ) {
+ return;
+ }
+
+ const root = {
+ path: expression,
+ contents: {
+ value: resultGrip,
+ front: getFront(result),
+ },
+ };
+ const properties = await client.loadObjectProperties(root, thread);
+
+ // The first time a popup is rendered, the mouse should be hovered
+ // on the token. If it happens to be hovered on whitespace, it should
+ // not render anything
+ if (!target.matches(":hover") && !isNodeTest()) {
+ return;
+ }
+
+ // Don't finish dispatching if another setPreview was started
+ if (previewCount != getPreviewCount(getState())) {
+ return;
+ }
+
+ dispatch({
+ type: "SET_PREVIEW",
+ cx,
+ value: {
+ expression,
+ resultGrip,
+ properties,
+ root,
+ location,
+ tokenPos,
+ cursorPos,
+ target,
+ },
+ });
+ };
+}
+
+export function clearPreview(cx) {
+ return ({ dispatch, getState, client }) => {
+ const currentSelection = getPreview(getState());
+ if (!currentSelection) {
+ return null;
+ }
+
+ return dispatch({
+ type: "CLEAR_PREVIEW",
+ cx,
+ });
+ };
+}
+
+export function setExceptionPreview(cx, target, tokenPos, codeMirror) {
+ return async ({ dispatch, getState }) => {
+ const cursorPos = target.getBoundingClientRect();
+
+ const match = findExpressionMatch(getState(), codeMirror, tokenPos);
+ if (!match) {
+ return;
+ }
+
+ const tokenColumnStart = match.location.start.column + 1;
+ const exception = getSelectedException(
+ getState(),
+ tokenPos.line,
+ tokenColumnStart
+ );
+ if (!exception) {
+ return;
+ }
+
+ dispatch({
+ type: "SET_PREVIEW",
+ cx,
+ value: {
+ exception,
+ location: match.location,
+ tokenPos,
+ cursorPos,
+ target,
+ },
+ });
+ };
+}