tor-browser

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

sources-content.js (4036B)


      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 /**
      6 * Sources content reducer.
      7 *
      8 * This store the textual content for each source.
      9 */
     10 
     11 import { pending, fulfilled, rejected } from "../utils/async-value";
     12 
     13 export function initialSourcesContentState() {
     14  return {
     15    /**
     16     * Text content of all the original sources.
     17     * This is large data, so this is only fetched on-demand for a subset of sources.
     18     * This state attribute is mutable in order to avoid cloning this possibly large map
     19     * on each new source. But selectors are never based on the map. Instead they only
     20     * query elements of the map.
     21     *
     22     * Map(source id => AsyncValue<String>)
     23     */
     24    mutableOriginalSourceTextContentMapBySourceId: new Map(),
     25 
     26    /**
     27     * Text content of all the generated sources.
     28     *
     29     * Map(source actor is => AsyncValue<String>)
     30     */
     31    mutableGeneratedSourceTextContentMapBySourceActorId: new Map(),
     32 
     33    /**
     34     * Incremental number that is bumped each time we navigate to a new page.
     35     *
     36     * This is used to better handle async race condition where we mix previous page data
     37     * with the new page. As sources are keyed by URL we may easily conflate the two page loads data.
     38     */
     39    epoch: 1,
     40  };
     41 }
     42 
     43 function update(state = initialSourcesContentState(), action) {
     44  switch (action.type) {
     45    case "LOAD_ORIGINAL_SOURCE_TEXT":
     46      if (!action.source) {
     47        throw new Error("Missing source");
     48      }
     49      return updateSourceTextContent(state, action);
     50 
     51    case "LOAD_GENERATED_SOURCE_TEXT":
     52      if (!action.sourceActor) {
     53        throw new Error("Missing source actor.");
     54      }
     55      return updateSourceTextContent(state, action);
     56 
     57    case "REMOVE_SOURCES":
     58      return removeAllSourceTextContentForSourcesAndActors(state, action);
     59  }
     60 
     61  return state;
     62 }
     63 
     64 /*
     65 * Update a source's loaded text content.
     66 */
     67 function updateSourceTextContent(state, action) {
     68  // If there was a navigation between the time the action was started and
     69  // completed, we don't want to update the store.
     70  if (action.epoch !== state.epoch) {
     71    return state;
     72  }
     73 
     74  let content;
     75  if (action.status === "start") {
     76    content = pending();
     77  } else if (action.status === "error") {
     78    content = rejected(action.error);
     79  } else if (typeof action.value.text === "string") {
     80    content = fulfilled({
     81      type: "text",
     82      value: action.value.text,
     83      contentType: action.value.contentType,
     84    });
     85  } else {
     86    content = fulfilled({
     87      type: "wasm",
     88      value: action.value.text,
     89    });
     90  }
     91 
     92  if (action.source && action.sourceActor) {
     93    throw new Error(
     94      "Both the source and the source actor should not exist at the same time"
     95    );
     96  }
     97 
     98  if (action.source) {
     99    state.mutableOriginalSourceTextContentMapBySourceId.set(
    100      action.source.id,
    101      content
    102    );
    103  }
    104 
    105  if (action.sourceActor) {
    106    state.mutableGeneratedSourceTextContentMapBySourceActorId.set(
    107      action.sourceActor.id,
    108      content
    109    );
    110  }
    111 
    112  return {
    113    ...state,
    114  };
    115 }
    116 
    117 function removeAllSourceTextContentForSourcesAndActors(state, action) {
    118  if (!action.sources.length && !action.actors.length) {
    119    return state;
    120  }
    121  const originalSizeBefore =
    122    state.mutableOriginalSourceTextContentMapBySourceId.size;
    123  for (const source of action.sources) {
    124    state.mutableOriginalSourceTextContentMapBySourceId.delete(source.id);
    125  }
    126  const generatedSizeBefore =
    127    state.mutableGeneratedSourceTextContentMapBySourceActorId.size;
    128  for (const actor of action.actors) {
    129    state.mutableGeneratedSourceTextContentMapBySourceActorId.delete(actor.id);
    130  }
    131  if (
    132    originalSizeBefore !=
    133      state.mutableOriginalSourceTextContentMapBySourceId.size ||
    134    generatedSizeBefore !=
    135      state.mutableGeneratedSourceTextContentMapBySourceActorId.size
    136  ) {
    137    return { ...state };
    138  }
    139  return state;
    140 }
    141 
    142 export default update;