diff options
Diffstat (limited to 'devtools/client/webconsole/middleware')
4 files changed, 237 insertions, 0 deletions
diff --git a/devtools/client/webconsole/middleware/event-telemetry.js b/devtools/client/webconsole/middleware/event-telemetry.js new file mode 100644 index 0000000000..bf5b203af6 --- /dev/null +++ b/devtools/client/webconsole/middleware/event-telemetry.js @@ -0,0 +1,129 @@ +/* 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/. */ + +"use strict"; + +const { + FILTER_TEXT_SET, + FILTER_TOGGLE, + DEFAULT_FILTERS_RESET, + EVALUATE_EXPRESSION, + MESSAGES_ADD, + PERSIST_TOGGLE, + REVERSE_SEARCH_INPUT_TOGGLE, + REVERSE_SEARCH_NEXT, + REVERSE_SEARCH_BACK, +} = require("resource://devtools/client/webconsole/constants.js"); + +/** + * Event telemetry middleware is responsible for logging specific events to telemetry. + */ +function eventTelemetryMiddleware(telemetry, store) { + return next => action => { + const oldState = store.getState(); + const res = next(action); + + const state = store.getState(); + + const filterChangeActions = [ + FILTER_TEXT_SET, + FILTER_TOGGLE, + DEFAULT_FILTERS_RESET, + ]; + + if (filterChangeActions.includes(action.type)) { + filterChange({ + action, + state, + oldState, + telemetry, + }); + } else if (action.type === MESSAGES_ADD) { + messagesAdd({ action, telemetry }); + } else if (action.type === PERSIST_TOGGLE) { + telemetry.recordEvent( + "persist_changed", + "webconsole", + String(state.ui.persistLogs) + ); + } else if (action.type === EVALUATE_EXPRESSION) { + // Send telemetry event. If we are in the browser toolbox we send -1 as the + // toolbox session id. + + telemetry.recordEvent("execute_js", "webconsole", null, { + lines: action.expression.split(/\n/).length, + input: state.ui.editor ? "multiline" : "inline", + }); + + if (action.from === "reverse-search") { + telemetry.recordEvent("reverse_search", "webconsole", null, { + functionality: "evaluate expression", + }); + } + } else if ( + action.type === REVERSE_SEARCH_INPUT_TOGGLE && + state.ui.reverseSearchInputVisible + ) { + telemetry.recordEvent("reverse_search", "webconsole", action.access, { + functionality: "open", + }); + } else if (action.type === REVERSE_SEARCH_NEXT) { + telemetry.recordEvent("reverse_search", "webconsole", action.access, { + functionality: "navigate next", + }); + } else if (action.type === REVERSE_SEARCH_BACK) { + telemetry.recordEvent("reverse_search", "webconsole", action.access, { + functionality: "navigate previous", + }); + } + + return res; + }; +} + +function filterChange({ action, state, oldState, telemetry }) { + const oldFilterState = oldState.filters; + const filterState = state.filters; + const activeFilters = []; + const inactiveFilters = []; + for (const [key, value] of Object.entries(filterState)) { + if (value) { + activeFilters.push(key); + } else { + inactiveFilters.push(key); + } + } + + let trigger; + if (action.type === FILTER_TOGGLE) { + trigger = action.filter; + } else if (action.type === DEFAULT_FILTERS_RESET) { + trigger = "reset"; + } else if (action.type === FILTER_TEXT_SET) { + if (oldFilterState.text !== "" && filterState.text !== "") { + return; + } + + trigger = "text"; + } + + telemetry.recordEvent("filters_changed", "webconsole", null, { + trigger, + active: activeFilters.join(","), + inactive: inactiveFilters.join(","), + }); +} + +function messagesAdd({ action, telemetry }) { + const { messages } = action; + for (const message of messages) { + if (message.level === "error" && message.source === "javascript") { + telemetry + .getKeyedHistogramById("DEVTOOLS_JAVASCRIPT_ERROR_DISPLAYED") + .add(message.errorMessageName || "Unknown", true); + } + } +} + +module.exports = eventTelemetryMiddleware; diff --git a/devtools/client/webconsole/middleware/history-persistence.js b/devtools/client/webconsole/middleware/history-persistence.js new file mode 100644 index 0000000000..b6b6ca9f7c --- /dev/null +++ b/devtools/client/webconsole/middleware/history-persistence.js @@ -0,0 +1,71 @@ +/* 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/. */ + +"use strict"; + +const { + APPEND_TO_HISTORY, + CLEAR_HISTORY, + EVALUATE_EXPRESSION, +} = require("resource://devtools/client/webconsole/constants.js"); + +const historyActions = require("resource://devtools/client/webconsole/actions/history.js"); + +loader.lazyRequireGetter( + this, + "asyncStorage", + "resource://devtools/shared/async-storage.js" +); + +/** + * History persistence middleware is responsible for loading + * and maintaining history of executed expressions in JSTerm. + */ +function historyPersistenceMiddleware(webConsoleUI, store) { + let historyLoaded = false; + asyncStorage.getItem("webConsoleHistory").then( + value => { + if (Array.isArray(value)) { + store.dispatch(historyActions.historyLoaded(value)); + } + historyLoaded = true; + }, + err => { + historyLoaded = true; + console.error(err); + } + ); + + return next => action => { + const res = next(action); + + const triggerStoreActions = [ + APPEND_TO_HISTORY, + CLEAR_HISTORY, + EVALUATE_EXPRESSION, + ]; + + // Save the current history entries when modified, but wait till + // entries from the previous session are loaded. + const { isPrivate } = + webConsoleUI.hud?.commands?.targetCommand?.targetFront?.targetForm || {}; + + if ( + !isPrivate && + historyLoaded && + triggerStoreActions.includes(action.type) + ) { + const state = store.getState(); + asyncStorage + .setItem("webConsoleHistory", state.history.entries) + .catch(e => { + console.error("Error when saving WebConsole input history", e); + }); + } + + return res; + }; +} + +module.exports = historyPersistenceMiddleware; diff --git a/devtools/client/webconsole/middleware/moz.build b/devtools/client/webconsole/middleware/moz.build new file mode 100644 index 0000000000..ecb2088ca4 --- /dev/null +++ b/devtools/client/webconsole/middleware/moz.build @@ -0,0 +1,10 @@ +# 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/. + +DevToolsModules( + "event-telemetry.js", + "history-persistence.js", + "performance-marker.js", +) diff --git a/devtools/client/webconsole/middleware/performance-marker.js b/devtools/client/webconsole/middleware/performance-marker.js new file mode 100644 index 0000000000..93fcee279c --- /dev/null +++ b/devtools/client/webconsole/middleware/performance-marker.js @@ -0,0 +1,27 @@ +/* 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/. */ + +"use strict"; + +const { + MESSAGES_ADD, +} = require("resource://devtools/client/webconsole/constants.js"); + +const { + createPerformanceMarkerMiddleware, +} = require("resource://devtools/client/shared/redux/middleware/performance-marker.js"); + +module.exports = function (sessionId) { + return createPerformanceMarkerMiddleware({ + [MESSAGES_ADD]: { + label: "WebconsoleAddMessages", + sessionId, + getMarkerDescription({ action, state }) { + const { messages } = action; + const totalMessageCount = state.messages.mutableMessagesById.size; + return `${messages.length} messages handled, store now has ${totalMessageCount} messages`; + }, + }, + }); +}; |