summaryrefslogtreecommitdiffstats
path: root/devtools/client/debugger/src/actions/sources/select.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/debugger/src/actions/sources/select.js')
-rw-r--r--devtools/client/debugger/src/actions/sources/select.js240
1 files changed, 240 insertions, 0 deletions
diff --git a/devtools/client/debugger/src/actions/sources/select.js b/devtools/client/debugger/src/actions/sources/select.js
new file mode 100644
index 0000000000..802c8cf021
--- /dev/null
+++ b/devtools/client/debugger/src/actions/sources/select.js
@@ -0,0 +1,240 @@
+/* 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
+
+/**
+ * Redux actions for the sources state
+ * @module actions/sources
+ */
+
+import { isOriginalId } from "devtools-source-map";
+
+import { getSourceFromId, getSourceWithContent } from "../../reducers/sources";
+import { tabExists } from "../../reducers/tabs";
+import { setSymbols } from "./symbols";
+import { setInScopeLines } from "../ast";
+import { closeActiveSearch, updateActiveFileSearch } from "../ui";
+import { togglePrettyPrint } from "./prettyPrint";
+import { addTab, closeTab } from "../tabs";
+import { loadSourceText } from "./loadSourceText";
+import { mapDisplayNames } from "../pause";
+import { setBreakableLines } from ".";
+
+import { prefs } from "../../utils/prefs";
+import { isMinified } from "../../utils/source";
+import { createLocation } from "../../utils/location";
+import { mapLocation } from "../../utils/source-maps";
+
+import {
+ getSource,
+ getSourceByURL,
+ getPrettySource,
+ getActiveSearch,
+ getSelectedLocation,
+ getSelectedSource,
+ canPrettyPrintSource,
+} from "../../selectors";
+
+import type {
+ SourceLocation,
+ PartialPosition,
+ SourceId,
+ Source,
+ Context,
+ URL,
+} from "../../types";
+import type { ThunkArgs } from "../types";
+import type { SourceAction } from "../types/SourceAction";
+
+export const setSelectedLocation = (
+ cx: Context,
+ source: Source,
+ location: SourceLocation
+): SourceAction => ({
+ type: "SET_SELECTED_LOCATION",
+ cx,
+ source,
+ location,
+});
+
+export const setPendingSelectedLocation = (
+ cx: Context,
+ url: URL,
+ options?: PartialPosition
+): SourceAction => ({
+ type: "SET_PENDING_SELECTED_LOCATION",
+ cx,
+ url,
+ line: options?.line,
+ column: options?.column,
+});
+
+export const clearSelectedLocation = (cx: Context): SourceAction => ({
+ type: "CLEAR_SELECTED_LOCATION",
+ cx,
+});
+
+/**
+ * Deterministically select a source that has a given URL. This will
+ * work regardless of the connection status or if the source exists
+ * yet.
+ *
+ * This exists mostly for external things to interact with the
+ * debugger.
+ *
+ * @memberof actions/sources
+ * @static
+ */
+export function selectSourceURL(
+ cx: Context,
+ url: URL,
+ options?: PartialPosition
+) {
+ return async ({ dispatch, getState, sourceMaps }: ThunkArgs) => {
+ const source = getSourceByURL(getState(), url);
+ if (!source) {
+ return dispatch(setPendingSelectedLocation(cx, url, options));
+ }
+
+ const sourceId = source.id;
+ const location = createLocation({ ...options, sourceId });
+ return dispatch(selectLocation(cx, location));
+ };
+}
+
+/**
+ * @memberof actions/sources
+ * @static
+ */
+export function selectSource(
+ cx: Context,
+ sourceId: SourceId,
+ options: PartialPosition = {}
+) {
+ return async ({ dispatch }: ThunkArgs) => {
+ const location = createLocation({ ...options, sourceId });
+ return dispatch(selectSpecificLocation(cx, location));
+ };
+}
+
+/**
+ * @memberof actions/sources
+ * @static
+ */
+export function selectLocation(
+ cx: Context,
+ location: SourceLocation,
+ { keepContext = true }: Object = {}
+) {
+ return async ({ dispatch, getState, sourceMaps, client }: ThunkArgs) => {
+ const currentSource = getSelectedSource(getState());
+
+ if (!client) {
+ // No connection, do nothing. This happens when the debugger is
+ // shut down too fast and it tries to display a default source.
+ return;
+ }
+
+ let source = getSource(getState(), location.sourceId);
+ if (!source) {
+ // If there is no source we deselect the current selected source
+ return dispatch(clearSelectedLocation(cx));
+ }
+
+ const activeSearch = getActiveSearch(getState());
+ if (activeSearch && activeSearch !== "file") {
+ dispatch(closeActiveSearch());
+ }
+
+ // Preserve the current source map context (original / generated)
+ // when navigting to a new location.
+ const selectedSource = getSelectedSource(getState());
+ if (
+ keepContext &&
+ selectedSource &&
+ selectedSource.isOriginal != isOriginalId(location.sourceId)
+ ) {
+ location = await mapLocation(getState(), sourceMaps, location);
+ source = getSourceFromId(getState(), location.sourceId);
+ }
+
+ if (!tabExists(getState(), source.id)) {
+ dispatch(addTab(source));
+ }
+
+ dispatch(setSelectedLocation(cx, source, location));
+
+ await dispatch(loadSourceText({ cx, source }));
+ await dispatch(setBreakableLines(cx, source.id));
+
+ const loadedSource = getSource(getState(), source.id);
+
+ if (!loadedSource) {
+ // If there was a navigation while we were loading the loadedSource
+ return;
+ }
+
+ const sourceWithContent = getSourceWithContent(getState(), source.id);
+
+ if (
+ keepContext &&
+ prefs.autoPrettyPrint &&
+ !getPrettySource(getState(), loadedSource.id) &&
+ canPrettyPrintSource(getState(), loadedSource.id) &&
+ isMinified(sourceWithContent)
+ ) {
+ await dispatch(togglePrettyPrint(cx, loadedSource.id));
+ dispatch(closeTab(cx, loadedSource));
+ }
+
+ await dispatch(setSymbols({ cx, source: loadedSource }));
+ dispatch(setInScopeLines(cx));
+
+ if (cx.isPaused) {
+ await dispatch(mapDisplayNames(cx));
+ }
+
+ // If a new source is selected update the file search results
+ const newSource = getSelectedSource(getState());
+ if (currentSource && currentSource !== newSource) {
+ dispatch(updateActiveFileSearch(cx));
+ }
+ };
+}
+
+/**
+ * @memberof actions/sources
+ * @static
+ */
+export function selectSpecificLocation(cx: Context, location: SourceLocation) {
+ return selectLocation(cx, location, { keepContext: false });
+}
+
+/**
+ * @memberof actions/sources
+ * @static
+ */
+export function jumpToMappedLocation(cx: Context, location: SourceLocation) {
+ return async function({ dispatch, getState, client, sourceMaps }: ThunkArgs) {
+ if (!client) {
+ return;
+ }
+
+ const pairedLocation = await mapLocation(getState(), sourceMaps, location);
+
+ return dispatch(selectSpecificLocation(cx, { ...pairedLocation }));
+ };
+}
+
+export function jumpToMappedSelectedLocation(cx: Context) {
+ return async function({ dispatch, getState }: ThunkArgs) {
+ const location = getSelectedLocation(getState());
+ if (!location) {
+ return;
+ }
+
+ await dispatch(jumpToMappedLocation(cx, location));
+ };
+}