tor-browser

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

statistics.js (5280B)


      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  FILTER_TAGS,
      9 } = require("resource://devtools/client/netmonitor/src/constants.js");
     10 const {
     11  Filters,
     12 } = require("resource://devtools/client/netmonitor/src/utils/filter-predicates.js");
     13 const {
     14  processNetworkUpdates,
     15  responseIsFresh,
     16 } = require("resource://devtools/client/netmonitor/src/utils/request-utils.js");
     17 const {
     18  ADD_REQUEST,
     19  UPDATE_REQUEST,
     20  CLEAR_REQUESTS,
     21  OPEN_STATISTICS,
     22 } = require("resource://devtools/client/netmonitor/src/constants.js");
     23 
     24 function initStatisticsData() {
     25  return FILTER_TAGS.map(type => ({
     26    cached: 0,
     27    count: 0,
     28    label: type,
     29    size: 0,
     30    transferredSize: 0,
     31    time: 0,
     32    nonBlockingTime: 0,
     33  }));
     34 }
     35 
     36 /**
     37 * This structure stores the statistics data for the empty browser cache (first visit to the site)
     38 * and the primed browser cache (subsequent visits to the site) scenarios.
     39 */
     40 function Statistics() {
     41  return {
     42    // This list of requests is used to help process the statistics data.
     43    // Changes to this list should not trigger updates.
     44    mutableRequests: [],
     45    // List of requests whose data has already been included in the
     46    // statistics data.
     47    mutableUsedRequests: new Set(),
     48    statisticsData: {
     49      emptyCacheData: initStatisticsData(),
     50      primedCacheData: initStatisticsData(),
     51    },
     52    // Tracks when the statistics panel is open so we only process requests
     53    // for the reducer then.
     54    statisticsPanelOpen: false,
     55  };
     56 }
     57 
     58 /**
     59 * This reducer is responsible for maintaining the statistics data.
     60 */
     61 function statisticsReducer(state = Statistics(), action) {
     62  switch (action.type) {
     63    case OPEN_STATISTICS: {
     64      if (state.statisticsPanelOpen !== action.open) {
     65        state.statisticsPanelOpen = action.open;
     66      }
     67      return state;
     68    }
     69    case ADD_REQUEST: {
     70      if (!state.statisticsPanelOpen) {
     71        return state;
     72      }
     73      return addRequest(state, action);
     74    }
     75 
     76    case UPDATE_REQUEST: {
     77      if (!state.statisticsPanelOpen) {
     78        return state;
     79      }
     80      const index = state.mutableRequests.findIndex(
     81        request => request.id === action.id
     82      );
     83      if (index === -1) {
     84        return state;
     85      }
     86      const request = state.mutableRequests[index];
     87      const updatedRequest = {
     88        ...request,
     89        ...processNetworkUpdates(action.data),
     90      };
     91      state.mutableRequests[index] = updatedRequest;
     92 
     93      return updateStatisticsData(state, state.mutableRequests[index]);
     94    }
     95 
     96    case CLEAR_REQUESTS: {
     97      return {
     98        ...Statistics(),
     99        statisticsPanelOpen: state.statisticsPanelOpen,
    100      };
    101    }
    102 
    103    default:
    104      return state;
    105  }
    106 }
    107 
    108 function addRequest(state, action) {
    109  // The target front is not used and cannot be serialized by redux
    110  // eslint-disable-next-line no-unused-vars
    111  const { targetFront, ...requestData } = action.data;
    112  const newRequest = {
    113    id: action.id,
    114    ...requestData,
    115  };
    116  state.mutableRequests.push(newRequest);
    117  return state;
    118 }
    119 
    120 function updateStatisticsData(state, request) {
    121  // Make sure all the data needed is included in the request
    122  // This avoids firing unnecessary request updates.
    123  if (
    124    request.contentSize == undefined ||
    125    !request.mimeType ||
    126    !request.eventTimings ||
    127    !request.responseHeaders ||
    128    request.status == undefined ||
    129    request.totalTime == undefined ||
    130    state.mutableUsedRequests.has(request.id)
    131  ) {
    132    return state;
    133  }
    134  const {
    135    statisticsData: { emptyCacheData, primedCacheData },
    136  } = state;
    137  // If non of the filter types are matched, defaults to "others"
    138  let dataType = 8;
    139  for (const [index, type] of FILTER_TAGS.entries()) {
    140    if (Filters[type](request)) {
    141      dataType = index;
    142    }
    143    if (Filters.xhr(request)) {
    144      // Verify XHR last, to categorize other mime types in their own blobs.
    145      // "xhr"
    146      dataType = 3;
    147    }
    148  }
    149 
    150  let newEmptyCacheData = [...emptyCacheData];
    151  let newPrimedCacheData = [...primedCacheData];
    152 
    153  newEmptyCacheData = setData(newEmptyCacheData, request, dataType, true);
    154  newPrimedCacheData = setData(newPrimedCacheData, request, dataType, false);
    155  state.mutableUsedRequests.add(request.id);
    156 
    157  return {
    158    mutableRequests: state.mutableRequests,
    159    mutableUsedRequests: state.mutableUsedRequests,
    160    statisticsData: {
    161      emptyCacheData: newEmptyCacheData,
    162      primedCacheData: newPrimedCacheData,
    163    },
    164    statisticsPanelOpen: state.statisticsPanelOpen,
    165  };
    166 }
    167 
    168 function setData(cacheData, request, dataType, emptyCache) {
    169  if (emptyCache || !responseIsFresh(request)) {
    170    cacheData[dataType].time += request.totalTime || 0;
    171    cacheData[dataType].size += request.contentSize || 0;
    172    cacheData[dataType].transferredSize += request.transferredSize || 0;
    173 
    174    const nonBlockingTime =
    175      request.eventTimings.totalTime -
    176      (request.eventTimings.timings?.blocked || 0);
    177    cacheData[dataType].nonBlockingTime += nonBlockingTime || 0;
    178  } else {
    179    cacheData[dataType].cached++;
    180  }
    181  cacheData[dataType].count++;
    182  return cacheData;
    183 }
    184 
    185 module.exports = {
    186  statisticsReducer,
    187 };