tor-browser

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

init-store.mjs (4751B)


      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 file,
      3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 import {
      6  actionCreators as ac,
      7  actionTypes as at,
      8  actionUtils as au,
      9 } from "../../common/Actions.mjs";
     10 // We disable import checking here as redux is installed via the npm packages
     11 // at the newtab level, rather than in the top-level package.json.
     12 // eslint-disable-next-line import/no-unresolved
     13 import { applyMiddleware, combineReducers, createStore } from "redux";
     14 
     15 export const MERGE_STORE_ACTION = "NEW_TAB_INITIAL_STATE";
     16 export const OUTGOING_MESSAGE_NAME = "ActivityStream:ContentToMain";
     17 export const INCOMING_MESSAGE_NAME = "ActivityStream:MainToContent";
     18 
     19 /**
     20 * A higher-order function which returns a reducer that, on MERGE_STORE action,
     21 * will return the action.data object merged into the previous state.
     22 *
     23 * For all other actions, it merely calls mainReducer.
     24 *
     25 * Because we want this to merge the entire state object, it's written as a
     26 * higher order function which takes the main reducer (itself often a call to
     27 * combineReducers) as a parameter.
     28 *
     29 * @param  {function} mainReducer reducer to call if action != MERGE_STORE_ACTION
     30 * @return {function}             a reducer that, on MERGE_STORE_ACTION action,
     31 *                                will return the action.data object merged
     32 *                                into the previous state, and the result
     33 *                                of calling mainReducer otherwise.
     34 */
     35 function mergeStateReducer(mainReducer) {
     36  return (prevState, action) => {
     37    if (action.type === MERGE_STORE_ACTION) {
     38      return { ...prevState, ...action.data };
     39    }
     40 
     41    return mainReducer(prevState, action);
     42  };
     43 }
     44 
     45 /**
     46 * messageMiddleware - Middleware that looks for SentToMain type actions, and sends them if necessary
     47 */
     48 const messageMiddleware = () => next => action => {
     49  const skipLocal = action.meta && action.meta.skipLocal;
     50  if (au.isSendToMain(action)) {
     51    RPMSendAsyncMessage(OUTGOING_MESSAGE_NAME, action);
     52  }
     53  if (!skipLocal) {
     54    next(action);
     55  }
     56 };
     57 
     58 export const rehydrationMiddleware = ({ getState }) => {
     59  // NB: The parameter here is MiddlewareAPI which looks like a Store and shares
     60  // the same getState, so attached properties are accessible from the store.
     61  getState.didRehydrate = false;
     62  getState.didRequestInitialState = false;
     63  return next => action => {
     64    if (getState.didRehydrate || window.__FROM_STARTUP_CACHE__) {
     65      // Startup messages can be safely ignored by the about:home document
     66      // stored in the startup cache.
     67      if (
     68        window.__FROM_STARTUP_CACHE__ &&
     69        action.meta &&
     70        action.meta.isStartup
     71      ) {
     72        return null;
     73      }
     74      return next(action);
     75    }
     76 
     77    const isMergeStoreAction = action.type === MERGE_STORE_ACTION;
     78    const isRehydrationRequest = action.type === at.NEW_TAB_STATE_REQUEST;
     79 
     80    if (isRehydrationRequest) {
     81      getState.didRequestInitialState = true;
     82      return next(action);
     83    }
     84 
     85    if (isMergeStoreAction) {
     86      getState.didRehydrate = true;
     87      return next(action);
     88    }
     89 
     90    // If init happened after our request was made, we need to re-request
     91    if (getState.didRequestInitialState && action.type === at.INIT) {
     92      return next(ac.AlsoToMain({ type: at.NEW_TAB_STATE_REQUEST }));
     93    }
     94 
     95    if (
     96      au.isBroadcastToContent(action) ||
     97      au.isSendToOneContent(action) ||
     98      au.isSendToPreloaded(action)
     99    ) {
    100      // Note that actions received before didRehydrate will not be dispatched
    101      // because this could negatively affect preloading and the the state
    102      // will be replaced by rehydration anyway.
    103      return null;
    104    }
    105 
    106    return next(action);
    107  };
    108 };
    109 
    110 /**
    111 * initStore - Create a store and listen for incoming actions
    112 *
    113 * @param  {object} reducers An object containing Redux reducers
    114 * @param  {object} intialState (optional) The initial state of the store, if desired
    115 * @return {object}          A redux store
    116 */
    117 export function initStore(reducers, initialState) {
    118  const store = createStore(
    119    mergeStateReducer(combineReducers(reducers)),
    120    initialState,
    121    globalThis.RPMAddMessageListener &&
    122      applyMiddleware(rehydrationMiddleware, messageMiddleware)
    123  );
    124 
    125  if (globalThis.RPMAddMessageListener) {
    126    globalThis.RPMAddMessageListener(INCOMING_MESSAGE_NAME, msg => {
    127      try {
    128        store.dispatch(msg.data);
    129      } catch (ex) {
    130        console.error("Content msg:", msg, "Dispatch error: ", ex);
    131        dump(
    132          `Content msg: ${JSON.stringify(msg)}\nDispatch error: ${ex}\n${
    133            ex.stack
    134          }`
    135        );
    136      }
    137    });
    138  }
    139 
    140  return store;
    141 }