Bug 1944416: Restore individual tabs from closed groups in closed windows r=dao,sessi...
[gecko.git] / browser / components / sessionstore / TabStateCache.sys.mjs
blob56e0acc06bbff3fc21091d8d8f5103996a7f7981
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/. */
5 /**
6  * A cache for tabs data.
7  *
8  * This cache implements a weak map from tabs (as XUL elements)
9  * to tab data (as objects).
10  *
11  * Note that we should never cache private data, as:
12  * - that data is used very seldom by SessionStore;
13  * - caching private data in addition to public data is memory consuming.
14  */
15 export var TabStateCache = Object.freeze({
16   /**
17    * Retrieves cached data for a given |tab| or associated |browser|.
18    *
19    * @param permanentKey (object)
20    *        The tab or browser to retrieve cached data for.
21    * @return (object)
22    *         The cached data stored for the given |tab|
23    *         or associated |browser|.
24    */
25   get(permanentKey) {
26     return TabStateCacheInternal.get(permanentKey);
27   },
29   /**
30    * Updates cached data for a given |tab| or associated |browser|.
31    *
32    * @param permanentKey (object)
33    *        The tab or browser belonging to the given tab data.
34    * @param newData (object)
35    *        The new data to be stored for the given |tab|
36    *        or associated |browser|.
37    */
38   update(permanentKey, newData) {
39     TabStateCacheInternal.update(permanentKey, newData);
40   },
41 });
43 var TabStateCacheInternal = {
44   _data: new WeakMap(),
46   /**
47    * Retrieves cached data for a given |tab| or associated |browser|.
48    *
49    * @param permanentKey (object)
50    *        The tab or browser to retrieve cached data for.
51    * @return (object)
52    *         The cached data stored for the given |tab|
53    *         or associated |browser|.
54    */
55   get(permanentKey) {
56     return this._data.get(permanentKey);
57   },
59   /**
60    * Helper function used by update (see below). For message size
61    * optimization sometimes we don't update the whole session storage
62    * only the values that have been changed.
63    *
64    * @param data (object)
65    *        The cached data where we want to update the changes.
66    * @param change (object)
67    *        The actual changed values per domain.
68    */
69   updatePartialStorageChange(data, change) {
70     if (!data.storage) {
71       data.storage = {};
72     }
74     let storage = data.storage;
75     for (let domain of Object.keys(change)) {
76       if (!change[domain]) {
77         // We were sent null in place of the change object, which means
78         // we should delete session storage entirely for this domain.
79         delete storage[domain];
80       } else {
81         for (let key of Object.keys(change[domain])) {
82           let value = change[domain][key];
83           if (value === null) {
84             if (storage[domain] && storage[domain][key]) {
85               delete storage[domain][key];
86             }
87           } else {
88             if (!storage[domain]) {
89               storage[domain] = {};
90             }
91             storage[domain][key] = value;
92           }
93         }
94       }
95     }
96   },
98   /**
99    * Helper function used by update (see below). For message size
100    * optimization sometimes we don't update the whole browser history
101    * only the current index and the tail of the history from a certain
102    * index (specified by change.fromIdx)
103    *
104    * @param data (object)
105    *        The cached data where we want to update the changes.
106    * @param change (object)
107    *        Object containing the tail of the history array, and
108    *        some additional metadata.
109    */
110   updatePartialHistoryChange(data, change) {
111     const kLastIndex = Number.MAX_SAFE_INTEGER - 1;
113     if (!data.history) {
114       data.history = { entries: [] };
115     }
117     let history = data.history;
118     for (let key of Object.keys(change)) {
119       if (key == "entries") {
120         if (change.fromIdx != kLastIndex) {
121           let start = change.fromIdx + 1;
122           history.entries.splice(start, Infinity, ...change.entries);
123         }
124       } else if (key != "fromIdx") {
125         history[key] = change[key];
126       }
127     }
128   },
130   /**
131    * Updates cached data for a given |tab| or associated |browser|.
132    *
133    * @param permanentKey (object)
134    *        The tab or browser belonging to the given tab data.
135    * @param newData (object)
136    *        The new data to be stored for the given |tab|
137    *        or associated |browser|.
138    */
139   update(permanentKey, newData) {
140     let data = this._data.get(permanentKey) || {};
142     for (let key of Object.keys(newData)) {
143       if (key == "storagechange") {
144         this.updatePartialStorageChange(data, newData.storagechange);
145         continue;
146       }
148       if (key == "historychange") {
149         this.updatePartialHistoryChange(data, newData.historychange);
150         continue;
151       }
153       let value = newData[key];
154       if (value === null) {
155         delete data[key];
156       } else {
157         data[key] = value;
158       }
159     }
161     this._data.set(permanentKey, data);
162   },