diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /devtools/client/netmonitor/src/actions/search.js | |
parent | Initial commit. (diff) | |
download | thunderbird-upstream.tar.xz thunderbird-upstream.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'devtools/client/netmonitor/src/actions/search.js')
-rw-r--r-- | devtools/client/netmonitor/src/actions/search.js | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/devtools/client/netmonitor/src/actions/search.js b/devtools/client/netmonitor/src/actions/search.js new file mode 100644 index 0000000000..97b123d361 --- /dev/null +++ b/devtools/client/netmonitor/src/actions/search.js @@ -0,0 +1,316 @@ +/* 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 { + ADD_SEARCH_QUERY, + ADD_SEARCH_RESULT, + CLEAR_SEARCH_RESULTS, + ADD_ONGOING_SEARCH, + OPEN_ACTION_BAR, + UPDATE_SEARCH_STATUS, + SEARCH_STATUS, + SET_TARGET_SEARCH_RESULT, + SELECT_ACTION_BAR_TAB, + TOGGLE_SEARCH_CASE_SENSITIVE_SEARCH, + PANELS, +} = require("resource://devtools/client/netmonitor/src/constants.js"); + +const { + getDisplayedRequests, + getOngoingSearch, + getSearchStatus, + getRequestById, +} = require("resource://devtools/client/netmonitor/src/selectors/index.js"); + +const { + selectRequest, +} = require("resource://devtools/client/netmonitor/src/actions/selection.js"); +const { + selectDetailsPanelTab, +} = require("resource://devtools/client/netmonitor/src/actions/ui.js"); +const { + fetchNetworkUpdatePacket, +} = require("resource://devtools/client/netmonitor/src/utils/request-utils.js"); +const { + searchInResource, +} = require("resource://devtools/client/netmonitor/src/workers/search/index.js"); + +/** + * Search through all resources. This is the main action exported + * from this module and consumed by Network panel UI. + */ +function search(connector, query) { + let canceled = false; + + // Instantiate an `ongoingSearch` function/object. It's responsible + // for triggering set of asynchronous steps like fetching + // data from the backend and performing search over it. + // This `ongoingSearch` is stored in the Search reducer, so it can + // be canceled if needed (e.g. when new search is executed). + const newOngoingSearch = async ({ dispatch, getState }) => { + const state = getState(); + + dispatch(stopOngoingSearch()); + + await dispatch(addOngoingSearch(newOngoingSearch)); + await dispatch(clearSearchResults()); + await dispatch(addSearchQuery(query)); + + dispatch(updateSearchStatus(SEARCH_STATUS.FETCHING)); + + // Loop over all displayed resources (in the sorted order), + // fetch all the details data and run search worker that + // search through the resource structure. + const requests = getDisplayedRequests(state); + for (const request of requests) { + if (canceled) { + return; + } + + // Fetch all data for the resource. + await loadResource(connector, request); + if (canceled) { + return; + } + + // The state changed, so make sure to get fresh new reference + // to the updated resource object. + const updatedResource = getRequestById(getState(), request.id); + await dispatch(searchResource(updatedResource, query)); + } + + dispatch(updateSearchStatus(SEARCH_STATUS.DONE)); + }; + + // Implement support for canceling (used e.g. when a new search + // is executed or the user stops the searching manually). + newOngoingSearch.cancel = () => { + canceled = true; + }; + + newOngoingSearch.isCanceled = () => { + return canceled; + }; + + return newOngoingSearch; +} + +/** + * Fetch all data related to the specified resource from the backend. + */ +async function loadResource(connector, resource) { + const updateTypes = [ + "responseHeaders", + "requestHeaders", + "responseCookies", + "requestCookies", + "requestPostData", + "responseContent", + "responseCache", + "stackTrace", + "securityInfo", + ]; + + return fetchNetworkUpdatePacket(connector.requestData, resource, updateTypes); +} + +/** + * Search through all data within the specified resource. + */ +function searchResource(resource, query) { + return async ({ dispatch, getState }) => { + const state = getState(); + const ongoingSearch = getOngoingSearch(state); + + const modifiers = { + caseSensitive: state.search.caseSensitive, + }; + + // Run search in a worker and wait for the results. The return + // value is an array with search occurrences. + const result = await searchInResource(resource, query, modifiers); + + if (!result.length || ongoingSearch.isCanceled()) { + return; + } + + dispatch(addSearchResult(resource, result)); + }; +} + +/** + * Add search query to the reducer. + */ +function addSearchResult(resource, result) { + return { + type: ADD_SEARCH_RESULT, + resource, + result, + }; +} + +/** + * Add search query to the reducer. + */ +function addSearchQuery(query) { + return { + type: ADD_SEARCH_QUERY, + query, + }; +} + +/** + * Clear all search results. + */ +function clearSearchResults() { + return { + type: CLEAR_SEARCH_RESULTS, + }; +} + +/** + * Used to clear and cancel an ongoing search. + * @returns {Function} + */ +function clearSearchResultAndCancel() { + return ({ dispatch, getState }) => { + dispatch(stopOngoingSearch()); + dispatch(clearSearchResults()); + }; +} + +/** + * Update status of the current search. + */ +function updateSearchStatus(status) { + return { + type: UPDATE_SEARCH_STATUS, + status, + }; +} + +/** + * Close the entire search panel. + */ +function closeSearch() { + return ({ dispatch, getState }) => { + dispatch(stopOngoingSearch()); + dispatch({ type: OPEN_ACTION_BAR, open: false }); + }; +} + +/** + * Open the entire search panel + * @returns {Function} + */ +function openSearch() { + return ({ dispatch, getState }) => { + dispatch({ type: OPEN_ACTION_BAR, open: true }); + + dispatch({ + type: SELECT_ACTION_BAR_TAB, + id: PANELS.SEARCH, + }); + }; +} + +/** + * Toggles case sensitive search + * @returns {Function} + */ +function toggleCaseSensitiveSearch() { + return ({ dispatch, getState }) => { + dispatch({ type: TOGGLE_SEARCH_CASE_SENSITIVE_SEARCH }); + }; +} + +/** + * Toggle visibility of search panel in network panel + */ +function toggleSearchPanel() { + return ({ dispatch, getState }) => { + const state = getState(); + + state.ui.networkActionOpen && + state.ui.selectedActionBarTabId === PANELS.SEARCH + ? dispatch({ type: OPEN_ACTION_BAR, open: false }) + : dispatch({ type: OPEN_ACTION_BAR, open: true }); + + dispatch({ + type: SELECT_ACTION_BAR_TAB, + id: PANELS.SEARCH, + }); + }; +} + +/** + * Append new search object into the reducer. The search object + * is cancellable and so, it implements `cancel` method. + */ +function addOngoingSearch(ongoingSearch) { + return { + type: ADD_ONGOING_SEARCH, + ongoingSearch, + }; +} + +/** + * Cancel the current ongoing search. + */ +function stopOngoingSearch() { + return ({ dispatch, getState }) => { + const state = getState(); + const ongoingSearch = getOngoingSearch(state); + const status = getSearchStatus(state); + + if (ongoingSearch && status !== SEARCH_STATUS.DONE) { + ongoingSearch.cancel(); + dispatch(updateSearchStatus(SEARCH_STATUS.CANCELED)); + } + }; +} + +/** + * This action is fired when the user selects a search result + * within the Search panel. It opens the details side bar and + * selects the right side panel to show the context of the + * clicked search result. + */ +function navigate(searchResult) { + return ({ dispatch, getState }) => { + // Store target search result in Search reducer. It's used + // for search result navigation within the side panels. + dispatch(setTargetSearchResult(searchResult)); + + // Preselect the right side panel. + dispatch(selectDetailsPanelTab(searchResult.panel)); + + // Select related request in the UI (it also opens the + // right side bar automatically). + dispatch(selectRequest(searchResult.parentResource.id)); + }; +} + +function setTargetSearchResult(searchResult) { + return { + type: SET_TARGET_SEARCH_RESULT, + searchResult, + }; +} + +module.exports = { + search, + closeSearch, + openSearch, + clearSearchResults, + addSearchQuery, + toggleSearchPanel, + navigate, + setTargetSearchResult, + toggleCaseSensitiveSearch, + clearSearchResultAndCancel, + stopOngoingSearch, +}; |