tor-browser

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

accessibles.js (4252B)


      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 "use strict";
      5 
      6 const {
      7  AUDIT,
      8  FETCH_CHILDREN,
      9  HIGHLIGHT,
     10  RESET,
     11  SELECT,
     12 } = require("resource://devtools/client/accessibility/constants.js");
     13 
     14 /**
     15 * Initial state definition
     16 */
     17 function getInitialState() {
     18  return new Map();
     19 }
     20 
     21 /**
     22 * Maintain a cache of received accessibles responses from the backend.
     23 */
     24 function accessibles(state = getInitialState(), action) {
     25  switch (action.type) {
     26    case FETCH_CHILDREN:
     27      return onReceiveChildren(state, action);
     28    case HIGHLIGHT:
     29    case SELECT:
     30      return onReceiveAncestry(state, action);
     31    case AUDIT:
     32      return onAudit(state, action);
     33    case RESET:
     34      return getInitialState();
     35    default:
     36      return state;
     37  }
     38 }
     39 
     40 function getActorID(accessible) {
     41  return accessible.actorID || accessible._form?.actor;
     42 }
     43 
     44 /**
     45 * If accessible is cached recursively remove all its children and remove itself
     46 * from cache.
     47 *
     48 * @param {Map}    cache      Previous state maintaining a cache of previously
     49 *                            fetched accessibles.
     50 * @param {object} accessible Accessible object to remove from cache.
     51 */
     52 function cleanupChild(cache, accessible) {
     53  const actorID = getActorID(accessible);
     54  const cached = cache.get(actorID);
     55  if (!cached) {
     56    return;
     57  }
     58 
     59  for (const child of cached.children) {
     60    cleanupChild(cache, child);
     61  }
     62 
     63  cache.delete(actorID);
     64 }
     65 
     66 /**
     67 * Determine if accessible in cache is stale. Accessible object is stale if its
     68 * cached children array has the size other than the value of its childCount
     69 * property that updates on accessible actor event.
     70 *
     71 * @param {Map}    cache      Previous state maintaining a cache of previously
     72 *                             fetched accessibles.
     73 * @param {object} accessible Accessible object to test for staleness.
     74 */
     75 function staleChildren(cache, accessible) {
     76  const cached = cache.get(getActorID(accessible));
     77  if (!cached) {
     78    return false;
     79  }
     80 
     81  return cached.children.length !== accessible.childCount;
     82 }
     83 
     84 function updateChildrenCache(cache, accessible, children) {
     85  const actorID = getActorID(accessible);
     86 
     87  if (cache.has(actorID)) {
     88    const cached = cache.get(actorID);
     89    for (const child of cached.children) {
     90      // If exhisting children cache includes an accessible that is not present
     91      // any more or if child accessible is stale remove it and all its children
     92      // from cache.
     93      if (!children.includes(child) || staleChildren(cache, child)) {
     94        cleanupChild(cache, child);
     95      }
     96    }
     97    cached.children = children;
     98    cache.set(actorID, cached);
     99  } else {
    100    cache.set(actorID, { children });
    101  }
    102 
    103  return cache;
    104 }
    105 
    106 function updateAncestry(cache, ancestry) {
    107  ancestry.forEach(({ accessible, children }) =>
    108    updateChildrenCache(cache, accessible, children)
    109  );
    110 
    111  return cache;
    112 }
    113 
    114 /**
    115 * Handles fetching of accessible children.
    116 *
    117 * @param {Map}     cache  Previous state maintaining a cache of previously
    118 *                         fetched accessibles.
    119 * @param {object}  action Redux action object.
    120 * @return {object} updated state
    121 */
    122 function onReceiveChildren(cache, action) {
    123  const { error, accessible, response: children } = action;
    124  if (!error) {
    125    return updateChildrenCache(new Map(cache), accessible, children);
    126  }
    127 
    128  if (!accessible.isDestroyed()) {
    129    console.warn(`Error fetching children: `, error);
    130    return cache;
    131  }
    132 
    133  const newCache = new Map(cache);
    134  cleanupChild(newCache, accessible);
    135  return newCache;
    136 }
    137 
    138 function onReceiveAncestry(cache, action) {
    139  const { error, response: ancestry } = action;
    140  if (error) {
    141    console.warn(`Error fetching ancestry: `, error);
    142    return cache;
    143  }
    144 
    145  return updateAncestry(new Map(cache), ancestry);
    146 }
    147 
    148 function onAudit(cache, action) {
    149  const { error, response: ancestries } = action;
    150  if (error) {
    151    console.warn(`Error performing an audit: `, error);
    152    return cache;
    153  }
    154 
    155  const newCache = new Map(cache);
    156  ancestries.forEach(ancestry => updateAncestry(newCache, ancestry));
    157 
    158  return newCache;
    159 }
    160 
    161 exports.accessibles = accessibles;