tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

search.js (8219B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 "use strict";
      6 
      7 const {
      8  ADD_SEARCH_QUERY,
      9  ADD_SEARCH_RESULT,
     10  CLEAR_SEARCH_RESULTS,
     11  ADD_ONGOING_SEARCH,
     12  OPEN_ACTION_BAR,
     13  UPDATE_SEARCH_STATUS,
     14  SEARCH_STATUS,
     15  SET_TARGET_SEARCH_RESULT,
     16  SELECT_ACTION_BAR_TAB,
     17  TOGGLE_SEARCH_CASE_SENSITIVE_SEARCH,
     18  PANELS,
     19 } = require("resource://devtools/client/netmonitor/src/constants.js");
     20 
     21 const {
     22  getDisplayedRequests,
     23  getOngoingSearch,
     24  getSearchStatus,
     25  getRequestById,
     26 } = require("resource://devtools/client/netmonitor/src/selectors/index.js");
     27 
     28 const {
     29  selectRequest,
     30 } = require("resource://devtools/client/netmonitor/src/actions/selection.js");
     31 const {
     32  selectDetailsPanelTab,
     33 } = require("resource://devtools/client/netmonitor/src/actions/ui.js");
     34 const {
     35  fetchNetworkUpdatePacket,
     36 } = require("resource://devtools/client/netmonitor/src/utils/request-utils.js");
     37 const {
     38  searchInResource,
     39 } = require("resource://devtools/client/netmonitor/src/workers/search/index.js");
     40 
     41 /**
     42 * Search through all resources. This is the main action exported
     43 * from this module and consumed by Network panel UI.
     44 */
     45 function search(connector, query) {
     46  let canceled = false;
     47 
     48  // Instantiate an `ongoingSearch` function/object. It's responsible
     49  // for triggering set of asynchronous steps like fetching
     50  // data from the backend and performing search over it.
     51  // This `ongoingSearch` is stored in the Search reducer, so it can
     52  // be canceled if needed (e.g. when new search is executed).
     53  const newOngoingSearch = async ({ dispatch, getState }) => {
     54    const state = getState();
     55 
     56    dispatch(stopOngoingSearch());
     57 
     58    await dispatch(addOngoingSearch(newOngoingSearch));
     59    await dispatch(clearSearchResults());
     60    await dispatch(addSearchQuery(query));
     61 
     62    dispatch(updateSearchStatus(SEARCH_STATUS.FETCHING));
     63 
     64    // Loop over all displayed resources (in the sorted order),
     65    // fetch all the details data and run search worker that
     66    // search through the resource structure.
     67    const requests = getDisplayedRequests(state);
     68    for (const request of requests) {
     69      if (canceled) {
     70        return;
     71      }
     72 
     73      // Ignore any request which have a now defunct target
     74      // as we wouldn't be able to fetch any lazy data for them.
     75      // Inconditionally allow searching for navigation request as it is bound to the previous target front
     76      if (
     77        request.innerWindowId &&
     78        !request.isNavigationRequest &&
     79        !connector.commands.watcherFront.getWindowGlobalTargetByInnerWindowId(
     80          request.innerWindowId
     81        )
     82      ) {
     83        continue;
     84      }
     85 
     86      // Fetch all data for the resource.
     87      await loadResource(connector, request);
     88      if (canceled) {
     89        return;
     90      }
     91 
     92      // The state changed, so make sure to get fresh new reference
     93      // to the updated resource object.
     94      const updatedResource = getRequestById(getState(), request.id);
     95      await dispatch(searchResource(updatedResource, query));
     96    }
     97 
     98    dispatch(updateSearchStatus(SEARCH_STATUS.DONE));
     99  };
    100 
    101  // Implement support for canceling (used e.g. when a new search
    102  // is executed or the user stops the searching manually).
    103  newOngoingSearch.cancel = () => {
    104    canceled = true;
    105  };
    106 
    107  newOngoingSearch.isCanceled = () => {
    108    return canceled;
    109  };
    110 
    111  return newOngoingSearch;
    112 }
    113 
    114 /**
    115 * Fetch all data related to the specified resource from the backend.
    116 */
    117 async function loadResource(connector, resource) {
    118  const updateTypes = [
    119    "responseHeaders",
    120    "requestHeaders",
    121    "responseCookies",
    122    "requestCookies",
    123    "requestPostData",
    124    "responseContent",
    125    "responseCache",
    126    "stackTrace",
    127    "securityInfo",
    128  ];
    129 
    130  return fetchNetworkUpdatePacket(connector.requestData, resource, updateTypes);
    131 }
    132 
    133 /**
    134 * Search through all data within the specified resource.
    135 */
    136 function searchResource(resource, query) {
    137  return async ({ dispatch, getState }) => {
    138    const state = getState();
    139    const ongoingSearch = getOngoingSearch(state);
    140 
    141    const modifiers = {
    142      caseSensitive: state.search.caseSensitive,
    143    };
    144 
    145    // Run search in a worker and wait for the results. The return
    146    // value is an array with search occurrences.
    147    const result = await searchInResource(resource, query, modifiers);
    148 
    149    if (!result.length || ongoingSearch.isCanceled()) {
    150      return;
    151    }
    152 
    153    dispatch(addSearchResult(resource, result));
    154  };
    155 }
    156 
    157 /**
    158 * Add search query to the reducer.
    159 */
    160 function addSearchResult(resource, result) {
    161  return {
    162    type: ADD_SEARCH_RESULT,
    163    resource,
    164    result,
    165  };
    166 }
    167 
    168 /**
    169 * Add search query to the reducer.
    170 */
    171 function addSearchQuery(query) {
    172  return {
    173    type: ADD_SEARCH_QUERY,
    174    query,
    175  };
    176 }
    177 
    178 /**
    179 * Clear all search results.
    180 */
    181 function clearSearchResults() {
    182  return {
    183    type: CLEAR_SEARCH_RESULTS,
    184  };
    185 }
    186 
    187 /**
    188 * Used to clear and cancel an ongoing search.
    189 *
    190 * @returns {Function}
    191 */
    192 function clearSearchResultAndCancel() {
    193  return ({ dispatch }) => {
    194    dispatch(stopOngoingSearch());
    195    dispatch(clearSearchResults());
    196  };
    197 }
    198 
    199 /**
    200 * Update status of the current search.
    201 */
    202 function updateSearchStatus(status) {
    203  return {
    204    type: UPDATE_SEARCH_STATUS,
    205    status,
    206  };
    207 }
    208 
    209 /**
    210 * Close the entire search panel.
    211 */
    212 function closeSearch() {
    213  return ({ dispatch }) => {
    214    dispatch(stopOngoingSearch());
    215    dispatch({ type: OPEN_ACTION_BAR, open: false });
    216  };
    217 }
    218 
    219 /**
    220 * Open the entire search panel
    221 *
    222 * @returns {Function}
    223 */
    224 function openSearch() {
    225  return ({ dispatch }) => {
    226    dispatch({ type: OPEN_ACTION_BAR, open: true });
    227 
    228    dispatch({
    229      type: SELECT_ACTION_BAR_TAB,
    230      id: PANELS.SEARCH,
    231    });
    232  };
    233 }
    234 
    235 /**
    236 * Toggles case sensitive search
    237 *
    238 * @returns {Function}
    239 */
    240 function toggleCaseSensitiveSearch() {
    241  return ({ dispatch }) => {
    242    dispatch({ type: TOGGLE_SEARCH_CASE_SENSITIVE_SEARCH });
    243  };
    244 }
    245 
    246 /**
    247 * Toggle visibility of search panel in network panel
    248 */
    249 function toggleSearchPanel() {
    250  return ({ dispatch, getState }) => {
    251    const state = getState();
    252 
    253    state.ui.networkActionOpen &&
    254    state.ui.selectedActionBarTabId === PANELS.SEARCH
    255      ? dispatch({ type: OPEN_ACTION_BAR, open: false })
    256      : dispatch({ type: OPEN_ACTION_BAR, open: true });
    257 
    258    dispatch({
    259      type: SELECT_ACTION_BAR_TAB,
    260      id: PANELS.SEARCH,
    261    });
    262  };
    263 }
    264 
    265 /**
    266 * Append new search object into the reducer. The search object
    267 * is cancellable and so, it implements `cancel` method.
    268 */
    269 function addOngoingSearch(ongoingSearch) {
    270  return {
    271    type: ADD_ONGOING_SEARCH,
    272    ongoingSearch,
    273  };
    274 }
    275 
    276 /**
    277 * Cancel the current ongoing search.
    278 */
    279 function stopOngoingSearch() {
    280  return ({ dispatch, getState }) => {
    281    const state = getState();
    282    const ongoingSearch = getOngoingSearch(state);
    283    const status = getSearchStatus(state);
    284 
    285    if (ongoingSearch && status !== SEARCH_STATUS.DONE) {
    286      ongoingSearch.cancel();
    287      dispatch(updateSearchStatus(SEARCH_STATUS.CANCELED));
    288    }
    289  };
    290 }
    291 
    292 /**
    293 * This action is fired when the user selects a search result
    294 * within the Search panel. It opens the details side bar and
    295 * selects the right side panel to show the context of the
    296 * clicked search result.
    297 */
    298 function navigate(searchResult) {
    299  return ({ dispatch }) => {
    300    // Store target search result in Search reducer. It's used
    301    // for search result navigation within the side panels.
    302    dispatch(setTargetSearchResult(searchResult));
    303 
    304    // Preselect the required side panel.
    305    dispatch(selectDetailsPanelTab(searchResult.panel));
    306 
    307    // Select related request in the UI (it also opens the
    308    // right side bar automatically).
    309    dispatch(selectRequest(searchResult.parentResource.id));
    310  };
    311 }
    312 
    313 function setTargetSearchResult(searchResult) {
    314  return {
    315    type: SET_TARGET_SEARCH_RESULT,
    316    searchResult,
    317  };
    318 }
    319 
    320 module.exports = {
    321  search,
    322  closeSearch,
    323  openSearch,
    324  clearSearchResults,
    325  addSearchQuery,
    326  toggleSearchPanel,
    327  navigate,
    328  setTargetSearchResult,
    329  toggleCaseSensitiveSearch,
    330  clearSearchResultAndCancel,
    331  stopOngoingSearch,
    332 };