DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (cdf352f02ac4)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["TabStateCache"];

/**
 * A cache for tabs data.
 *
 * This cache implements a weak map from tabs (as XUL elements)
 * to tab data (as objects).
 *
 * Note that we should never cache private data, as:
 * - that data is used very seldom by SessionStore;
 * - caching private data in addition to public data is memory consuming.
 */
var TabStateCache = Object.freeze({
  /**
   * Retrieves cached data for a given |tab| or associated |browser|.
   *
   * @param browserOrTab (xul:tab or xul:browser)
   *        The tab or browser to retrieve cached data for.
   * @return (object)
   *         The cached data stored for the given |tab|
   *         or associated |browser|.
   */
  get(browserOrTab) {
    return TabStateCacheInternal.get(browserOrTab);
  },

  /**
   * Updates cached data for a given |tab| or associated |browser|.
   *
   * @param browserOrTab (xul:tab or xul:browser)
   *        The tab or browser belonging to the given tab data.
   * @param newData (object)
   *        The new data to be stored for the given |tab|
   *        or associated |browser|.
   */
  update(browserOrTab, newData) {
    TabStateCacheInternal.update(browserOrTab, newData);
  },
});

var TabStateCacheInternal = {
  _data: new WeakMap(),

  /**
   * Retrieves cached data for a given |tab| or associated |browser|.
   *
   * @param browserOrTab (xul:tab or xul:browser)
   *        The tab or browser to retrieve cached data for.
   * @return (object)
   *         The cached data stored for the given |tab|
   *         or associated |browser|.
   */
  get(browserOrTab) {
    return this._data.get(browserOrTab.permanentKey);
  },

  /**
   * Helper function used by update (see below). For message size
   * optimization sometimes we don't update the whole session storage
   * only the values that have been changed.
   *
   * @param data (object)
   *        The cached data where we want to update the changes.
   * @param change (object)
   *        The actual changed values per domain.
   */
  updatePartialStorageChange(data, change) {
    if (!data.storage) {
      data.storage = {};
    }

    let storage = data.storage;
    for (let domain of Object.keys(change)) {
      if (!change[domain]) {
        // We were sent null in place of the change object, which means
        // we should delete session storage entirely for this domain.
        delete storage[domain];
      } else {
        for (let key of Object.keys(change[domain])) {
          let value = change[domain][key];
          if (value === null) {
            if (storage[domain] && storage[domain][key]) {
              delete storage[domain][key];
            }
          } else {
            if (!storage[domain]) {
              storage[domain] = {};
            }
            storage[domain][key] = value;
          }
        }
      }
    }
  },

  /**
   * Helper function used by update (see below). For message size
   * optimization sometimes we don't update the whole browser history
   * only the current index and the tail of the history from a certain
   * index (specified by change.fromIdx)
   *
   * @param data (object)
   *        The cached data where we want to update the changes.
   * @param change (object)
   *        Object containing the tail of the history array, and
   *        some additional metadata.
   */
  updatePartialHistoryChange(data, change) {
    const kLastIndex = Number.MAX_SAFE_INTEGER - 1;

    if (!data.history) {
      data.history = { entries: [] };
    }

    let history = data.history;
    let toIdx = history.entries.length;
    if ("toIdx" in change) {
      toIdx = Math.min(toIdx, change.toIdx + 1);
    }

    for (let key of Object.keys(change)) {
      if (key == "entries") {
        if (change.fromIdx != kLastIndex) {
          let start = change.fromIdx + 1;
          history.entries.splice.apply(
            history.entries,
            [start, toIdx - start].concat(change.entries)
          );
        }
      } else if (key != "fromIdx" && key != "toIdx") {
        history[key] = change[key];
      }
    }
  },

  /**
   * Updates cached data for a given |tab| or associated |browser|.
   *
   * @param browserOrTab (xul:tab or xul:browser)
   *        The tab or browser belonging to the given tab data.
   * @param newData (object)
   *        The new data to be stored for the given |tab|
   *        or associated |browser|.
   */
  update(browserOrTab, newData) {
    let data = this._data.get(browserOrTab.permanentKey) || {};

    for (let key of Object.keys(newData)) {
      if (key == "storagechange") {
        this.updatePartialStorageChange(data, newData.storagechange);
        continue;
      }

      if (key == "historychange") {
        this.updatePartialHistoryChange(data, newData.historychange);
        continue;
      }

      let value = newData[key];
      if (value === null) {
        delete data[key];
      } else {
        data[key] = value;
      }
    }

    this._data.set(browserOrTab.permanentKey, data);
  },
};