summaryrefslogtreecommitdiffstats
path: root/devtools/client/netmonitor/src/actions/search.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--devtools/client/netmonitor/src/actions/search.js316
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,
+};