summaryrefslogtreecommitdiffstats
path: root/devtools/client/debugger/src/actions/utils/middleware
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /devtools/client/debugger/src/actions/utils/middleware
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'devtools/client/debugger/src/actions/utils/middleware')
-rw-r--r--devtools/client/debugger/src/actions/utils/middleware/context.js37
-rw-r--r--devtools/client/debugger/src/actions/utils/middleware/log.js116
-rw-r--r--devtools/client/debugger/src/actions/utils/middleware/moz.build15
-rw-r--r--devtools/client/debugger/src/actions/utils/middleware/promise.js122
-rw-r--r--devtools/client/debugger/src/actions/utils/middleware/thunk.js26
-rw-r--r--devtools/client/debugger/src/actions/utils/middleware/timing.js28
-rw-r--r--devtools/client/debugger/src/actions/utils/middleware/wait-service.js65
7 files changed, 409 insertions, 0 deletions
diff --git a/devtools/client/debugger/src/actions/utils/middleware/context.js b/devtools/client/debugger/src/actions/utils/middleware/context.js
new file mode 100644
index 0000000000..4a9ee09024
--- /dev/null
+++ b/devtools/client/debugger/src/actions/utils/middleware/context.js
@@ -0,0 +1,37 @@
+/* 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 {
+ validateNavigateContext,
+ validateContext,
+} from "../../../utils/context";
+
+import type { ThunkArgs } from "../../types";
+
+function validateActionContext(getState, action): void {
+ if (action.type == "COMMAND" && action.status == "done") {
+ // The thread will have resumed execution since the action was initiated,
+ // so just make sure we haven't navigated.
+ validateNavigateContext(getState(), action.cx);
+ return;
+ }
+
+ // Validate using all available information in the context.
+ validateContext(getState(), action.cx);
+}
+
+// Middleware which looks for actions that have a cx property and ignores
+// them if the context is no longer valid.
+function context({ dispatch, getState }: ThunkArgs) {
+ return (next: Function) => (action: Object) => {
+ if ("cx" in action) {
+ validateActionContext(getState, action);
+ }
+ return next(action);
+ };
+}
+
+export { context };
diff --git a/devtools/client/debugger/src/actions/utils/middleware/log.js b/devtools/client/debugger/src/actions/utils/middleware/log.js
new file mode 100644
index 0000000000..3b688c090f
--- /dev/null
+++ b/devtools/client/debugger/src/actions/utils/middleware/log.js
@@ -0,0 +1,116 @@
+/* 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
+
+// $FlowIgnore
+import flags from "devtools/shared/flags";
+import type { ThunkArgs } from "../../types";
+import { prefs } from "../../../utils/prefs";
+
+const ignoreList = [
+ "ADD_BREAKPOINT_POSITIONS",
+ "SET_SYMBOLS",
+ "OUT_OF_SCOPE_LOCATIONS",
+ "MAP_SCOPES",
+ "MAP_FRAMES",
+ "ADD_SCOPES",
+ "IN_SCOPE_LINES",
+ "REMOVE_BREAKPOINT",
+ "NODE_PROPERTIES_LOADED",
+ "SET_FOCUSED_SOURCE_ITEM",
+ "NODE_EXPAND",
+ "IN_SCOPE_LINES",
+ "SET_PREVIEW",
+];
+
+function cloneAction(action: any) {
+ action = action || {};
+ action = { ...action };
+
+ // ADD_TAB, ...
+ if (action.source?.text) {
+ const source = { ...action.source, text: "" };
+ action.source = source;
+ }
+
+ if (action.sources) {
+ const sources = action.sources.slice(0, 20).map(source => {
+ const url = !source.url || source.url.includes("data:") ? "" : source.url;
+ return { ...source, url };
+ });
+ action.sources = sources;
+ }
+
+ // LOAD_SOURCE_TEXT
+ if (action.text) {
+ action.text = "";
+ }
+
+ if (action.value?.text) {
+ const value = { ...action.value, text: "" };
+ action.value = value;
+ }
+
+ return action;
+}
+
+function formatPause(pause) {
+ return {
+ ...pause,
+ pauseInfo: { why: pause.why },
+ scopes: [],
+ loadedObjects: [],
+ };
+}
+
+function serializeAction(action) {
+ try {
+ action = cloneAction(action);
+ if (ignoreList.includes(action.type)) {
+ action = {};
+ }
+
+ if (action.type === "PAUSED") {
+ action = formatPause(action);
+ }
+
+ const serializer = function(key, value) {
+ // Serialize Object/LongString fronts
+ if (value?.getGrip) {
+ return value.getGrip();
+ }
+ return value;
+ };
+
+ // dump(`> ${action.type}...\n ${JSON.stringify(action, serializer)}\n`);
+ return JSON.stringify(action, serializer);
+ } catch (e) {
+ console.error(e);
+ return "";
+ }
+}
+
+/**
+ * A middleware that logs all actions coming through the system
+ * to the console.
+ */
+export function log({ dispatch, getState }: ThunkArgs) {
+ return (next: any) => (action: any) => {
+ const asyncMsg = !action.status ? "" : `[${action.status}]`;
+
+ if (prefs.logActions) {
+ if (flags.testing) {
+ // $FlowIgnore
+ dump(
+ `[ACTION] ${action.type} ${asyncMsg} - ${serializeAction(action)}\n`
+ );
+ } else {
+ console.log(action, asyncMsg);
+ }
+ }
+
+ next(action);
+ };
+}
diff --git a/devtools/client/debugger/src/actions/utils/middleware/moz.build b/devtools/client/debugger/src/actions/utils/middleware/moz.build
new file mode 100644
index 0000000000..f46a0bb725
--- /dev/null
+++ b/devtools/client/debugger/src/actions/utils/middleware/moz.build
@@ -0,0 +1,15 @@
+# vim: set filetype=python:
+# 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/.
+
+DIRS += []
+
+CompiledModules(
+ "context.js",
+ "log.js",
+ "promise.js",
+ "thunk.js",
+ "timing.js",
+ "wait-service.js",
+)
diff --git a/devtools/client/debugger/src/actions/utils/middleware/promise.js b/devtools/client/debugger/src/actions/utils/middleware/promise.js
new file mode 100644
index 0000000000..46c73c7325
--- /dev/null
+++ b/devtools/client/debugger/src/actions/utils/middleware/promise.js
@@ -0,0 +1,122 @@
+/* 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 { fromPairs, toPairs } from "lodash";
+import { executeSoon } from "../../../utils/DevToolsUtils";
+import type { ThunkArgs } from "../../types";
+
+type BasePromiseAction = {|
+ +"@@dispatch/promise": Promise<mixed>,
+|};
+
+export type StartPromiseAction = {|
+ ...BasePromiseAction,
+ +status: "start",
+|};
+
+export type DonePromiseAction = {|
+ ...BasePromiseAction,
+ +status: "done",
+ +value: any,
+|};
+
+export type ErrorPromiseAction = {|
+ ...BasePromiseAction,
+ +status: "error",
+ +error: any,
+|};
+
+import {
+ pending,
+ rejected,
+ fulfilled,
+ type AsyncValue,
+} from "../../../utils/async-value";
+export function asyncActionAsValue<T>(
+ action: PromiseAction<mixed, T>
+): AsyncValue<T> {
+ if (action.status === "start") {
+ return pending();
+ }
+ if (action.status === "error") {
+ return rejected(action.error);
+ }
+ return fulfilled(action.value);
+}
+
+export type PromiseAction<+Action, Value = any> =
+ // | {| ...Action, "@@dispatch/promise": Promise<Object> |}
+ | {|
+ ...BasePromiseAction,
+ ...Action,
+ +status: "start",
+ value: void,
+ |}
+ | {|
+ ...BasePromiseAction,
+ ...Action,
+ +status: "done",
+ +value: Value,
+ |}
+ | {|
+ ...BasePromiseAction,
+ ...Action,
+ +status: "error",
+ +error?: any,
+ value: void,
+ |};
+
+let seqIdVal = 1;
+
+function seqIdGen() {
+ return seqIdVal++;
+}
+
+function filterAction(action: Object): Object {
+ return fromPairs(toPairs(action).filter(pair => pair[0] !== PROMISE));
+}
+
+function promiseMiddleware({
+ dispatch,
+ getState,
+}: ThunkArgs): Function | Promise<mixed> {
+ return (next: Function) => (action: Object) => {
+ if (!(PROMISE in action)) {
+ return next(action);
+ }
+
+ const promiseInst = action[PROMISE];
+ const seqId = seqIdGen().toString();
+
+ // Create a new action that doesn't have the promise field and has
+ // the `seqId` field that represents the sequence id
+ action = { ...filterAction(action), seqId };
+
+ dispatch({ ...action, status: "start" });
+
+ // Return the promise so action creators can still compose if they
+ // want to.
+ return Promise.resolve(promiseInst)
+ .finally(() => new Promise(resolve => executeSoon(resolve)))
+ .then(
+ value => {
+ dispatch({ ...action, status: "done", value: value });
+ return value;
+ },
+ error => {
+ dispatch({
+ ...action,
+ status: "error",
+ error: error.message || error,
+ });
+ return Promise.reject(error);
+ }
+ );
+ };
+}
+
+export const PROMISE = "@@dispatch/promise";
+export { promiseMiddleware as promise };
diff --git a/devtools/client/debugger/src/actions/utils/middleware/thunk.js b/devtools/client/debugger/src/actions/utils/middleware/thunk.js
new file mode 100644
index 0000000000..a4d63f80e7
--- /dev/null
+++ b/devtools/client/debugger/src/actions/utils/middleware/thunk.js
@@ -0,0 +1,26 @@
+/* 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 type { ThunkArgs, ActionType } from "../../types";
+
+/**
+ * A middleware that allows thunks (functions) to be dispatched. If
+ * it's a thunk, it is called with an argument that contains
+ * `dispatch`, `getState`, and any additional args passed in via the
+ * middleware constructure. This allows the action to create multiple
+ * actions (most likely asynchronously).
+ */
+export function thunk(makeArgs: any) {
+ return ({ dispatch, getState }: ThunkArgs) => {
+ const args = { dispatch, getState };
+
+ return (next: Function) => (action: ActionType) => {
+ return typeof action === "function"
+ ? action(makeArgs ? makeArgs(args, getState()) : args)
+ : next(action);
+ };
+ };
+}
diff --git a/devtools/client/debugger/src/actions/utils/middleware/timing.js b/devtools/client/debugger/src/actions/utils/middleware/timing.js
new file mode 100644
index 0000000000..84661efb57
--- /dev/null
+++ b/devtools/client/debugger/src/actions/utils/middleware/timing.js
@@ -0,0 +1,28 @@
+/* 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 middleware that sets performance markers for all actions such that they
+ * will appear in performance tooling under the User Timing API
+ */
+
+const mark = window.performance?.mark
+ ? window.performance.mark.bind(window.performance)
+ : a => {};
+
+const measure = window.performance?.measure
+ ? window.performance.measure.bind(window.performance)
+ : (a, b, c) => {};
+
+export function timing(store: any) {
+ return (next: any) => (action: any) => {
+ mark(`${action.type}_start`);
+ const result = next(action);
+ mark(`${action.type}_end`);
+ measure(`${action.type}`, `${action.type}_start`, `${action.type}_end`);
+ return result;
+ };
+}
diff --git a/devtools/client/debugger/src/actions/utils/middleware/wait-service.js b/devtools/client/debugger/src/actions/utils/middleware/wait-service.js
new file mode 100644
index 0000000000..0d83eee91c
--- /dev/null
+++ b/devtools/client/debugger/src/actions/utils/middleware/wait-service.js
@@ -0,0 +1,65 @@
+/* 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
+
+/**
+ * A middleware which acts like a service, because it is stateful
+ * and "long-running" in the background. It provides the ability
+ * for actions to install a function to be run once when a specific
+ * condition is met by an action coming through the system. Think of
+ * it as a thunk that blocks until the condition is met. Example:
+ *
+ * ```js
+ * const services = { WAIT_UNTIL: require('wait-service').NAME };
+ *
+ * { type: services.WAIT_UNTIL,
+ * predicate: action => action.type === "ADD_ITEM",
+ * run: (dispatch, getState, action) => {
+ * // Do anything here. You only need to accept the arguments
+ * // if you need them. `action` is the action that satisfied
+ * // the predicate.
+ * }
+ * }
+ * ```
+ */
+export const NAME = "@@service/waitUntil";
+import type { ThunkArgs } from "../../types";
+
+export function waitUntilService({ dispatch, getState }: ThunkArgs) {
+ let pending = [];
+
+ function checkPending(action) {
+ const readyRequests = [];
+ const stillPending = [];
+
+ // Find the pending requests whose predicates are satisfied with
+ // this action. Wait to run the requests until after we update the
+ // pending queue because the request handler may synchronously
+ // dispatch again and run this service (that use case is
+ // completely valid).
+ for (const request of pending) {
+ if (request.predicate(action)) {
+ readyRequests.push(request);
+ } else {
+ stillPending.push(request);
+ }
+ }
+
+ pending = stillPending;
+ for (const request of readyRequests) {
+ request.run(dispatch, getState, action);
+ }
+ }
+
+ return (next: Function) => (action: Object) => {
+ if (action.type === NAME) {
+ pending.push(action);
+ return null;
+ }
+ const result = next(action);
+ checkPending(action);
+ return result;
+ };
+}