browser/components/sessionstore/src/TabState.jsm

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 "use strict";
michael@0 6
michael@0 7 this.EXPORTED_SYMBOLS = ["TabState"];
michael@0 8
michael@0 9 const Cu = Components.utils;
michael@0 10
michael@0 11 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
michael@0 12 Cu.import("resource://gre/modules/Promise.jsm", this);
michael@0 13 Cu.import("resource://gre/modules/Task.jsm", this);
michael@0 14
michael@0 15 XPCOMUtils.defineLazyModuleGetter(this, "console",
michael@0 16 "resource://gre/modules/devtools/Console.jsm");
michael@0 17 XPCOMUtils.defineLazyModuleGetter(this, "PrivacyFilter",
michael@0 18 "resource:///modules/sessionstore/PrivacyFilter.jsm");
michael@0 19 XPCOMUtils.defineLazyModuleGetter(this, "TabStateCache",
michael@0 20 "resource:///modules/sessionstore/TabStateCache.jsm");
michael@0 21 XPCOMUtils.defineLazyModuleGetter(this, "TabAttributes",
michael@0 22 "resource:///modules/sessionstore/TabAttributes.jsm");
michael@0 23 XPCOMUtils.defineLazyModuleGetter(this, "Utils",
michael@0 24 "resource:///modules/sessionstore/Utils.jsm");
michael@0 25
michael@0 26 /**
michael@0 27 * Module that contains tab state collection methods.
michael@0 28 */
michael@0 29 this.TabState = Object.freeze({
michael@0 30 setSyncHandler: function (browser, handler) {
michael@0 31 TabStateInternal.setSyncHandler(browser, handler);
michael@0 32 },
michael@0 33
michael@0 34 update: function (browser, data) {
michael@0 35 TabStateInternal.update(browser, data);
michael@0 36 },
michael@0 37
michael@0 38 flush: function (browser) {
michael@0 39 TabStateInternal.flush(browser);
michael@0 40 },
michael@0 41
michael@0 42 flushWindow: function (window) {
michael@0 43 TabStateInternal.flushWindow(window);
michael@0 44 },
michael@0 45
michael@0 46 collect: function (tab) {
michael@0 47 return TabStateInternal.collect(tab);
michael@0 48 },
michael@0 49
michael@0 50 clone: function (tab) {
michael@0 51 return TabStateInternal.clone(tab);
michael@0 52 }
michael@0 53 });
michael@0 54
michael@0 55 let TabStateInternal = {
michael@0 56 // A map (xul:browser -> handler) that maps a tab to the
michael@0 57 // synchronous collection handler object for that tab.
michael@0 58 // See SyncHandler in content-sessionStore.js.
michael@0 59 _syncHandlers: new WeakMap(),
michael@0 60
michael@0 61 // A map (xul:browser -> int) that maps a browser to the
michael@0 62 // last "SessionStore:update" message ID we received for it.
michael@0 63 _latestMessageID: new WeakMap(),
michael@0 64
michael@0 65 /**
michael@0 66 * Install the sync handler object from a given tab.
michael@0 67 */
michael@0 68 setSyncHandler: function (browser, handler) {
michael@0 69 this._syncHandlers.set(browser.permanentKey, handler);
michael@0 70 this._latestMessageID.set(browser.permanentKey, 0);
michael@0 71 },
michael@0 72
michael@0 73 /**
michael@0 74 * Processes a data update sent by the content script.
michael@0 75 */
michael@0 76 update: function (browser, {id, data}) {
michael@0 77 // Only ever process messages that have an ID higher than the last one we
michael@0 78 // saw. This ensures we don't use stale data that has already been received
michael@0 79 // synchronously.
michael@0 80 if (id > this._latestMessageID.get(browser.permanentKey)) {
michael@0 81 this._latestMessageID.set(browser.permanentKey, id);
michael@0 82 TabStateCache.update(browser, data);
michael@0 83 }
michael@0 84 },
michael@0 85
michael@0 86 /**
michael@0 87 * Flushes all data currently queued in the given browser's content script.
michael@0 88 */
michael@0 89 flush: function (browser) {
michael@0 90 if (this._syncHandlers.has(browser.permanentKey)) {
michael@0 91 let lastID = this._latestMessageID.get(browser.permanentKey);
michael@0 92 this._syncHandlers.get(browser.permanentKey).flush(lastID);
michael@0 93 }
michael@0 94 },
michael@0 95
michael@0 96 /**
michael@0 97 * Flushes queued content script data for all browsers of a given window.
michael@0 98 */
michael@0 99 flushWindow: function (window) {
michael@0 100 for (let browser of window.gBrowser.browsers) {
michael@0 101 this.flush(browser);
michael@0 102 }
michael@0 103 },
michael@0 104
michael@0 105 /**
michael@0 106 * Collect data related to a single tab, synchronously.
michael@0 107 *
michael@0 108 * @param tab
michael@0 109 * tabbrowser tab
michael@0 110 *
michael@0 111 * @returns {TabData} An object with the data for this tab. If the
michael@0 112 * tab has not been invalidated since the last call to
michael@0 113 * collect(aTab), the same object is returned.
michael@0 114 */
michael@0 115 collect: function (tab) {
michael@0 116 return this._collectBaseTabData(tab);
michael@0 117 },
michael@0 118
michael@0 119 /**
michael@0 120 * Collect data related to a single tab, including private data.
michael@0 121 * Use with caution.
michael@0 122 *
michael@0 123 * @param tab
michael@0 124 * tabbrowser tab
michael@0 125 *
michael@0 126 * @returns {object} An object with the data for this tab. This data is never
michael@0 127 * cached, it will always be read from the tab and thus be
michael@0 128 * up-to-date.
michael@0 129 */
michael@0 130 clone: function (tab) {
michael@0 131 return this._collectBaseTabData(tab, {includePrivateData: true});
michael@0 132 },
michael@0 133
michael@0 134 /**
michael@0 135 * Collects basic tab data for a given tab.
michael@0 136 *
michael@0 137 * @param tab
michael@0 138 * tabbrowser tab
michael@0 139 * @param options (object)
michael@0 140 * {includePrivateData: true} to always include private data
michael@0 141 *
michael@0 142 * @returns {object} An object with the basic data for this tab.
michael@0 143 */
michael@0 144 _collectBaseTabData: function (tab, options) {
michael@0 145 let tabData = {entries: [], lastAccessed: tab.lastAccessed };
michael@0 146 let browser = tab.linkedBrowser;
michael@0 147
michael@0 148 if (!browser || !browser.currentURI) {
michael@0 149 // can happen when calling this function right after .addTab()
michael@0 150 return tabData;
michael@0 151 }
michael@0 152 if (browser.__SS_data) {
michael@0 153 // Use the data to be restored when the tab hasn't been
michael@0 154 // completely loaded. We clone the data, since we're updating it
michael@0 155 // here and the caller may update it further.
michael@0 156 tabData = Utils.shallowCopy(browser.__SS_data);
michael@0 157 if (tab.pinned)
michael@0 158 tabData.pinned = true;
michael@0 159 else
michael@0 160 delete tabData.pinned;
michael@0 161 tabData.hidden = tab.hidden;
michael@0 162
michael@0 163 // If __SS_extdata is set then we'll use that since it might be newer.
michael@0 164 if (tab.__SS_extdata)
michael@0 165 tabData.extData = tab.__SS_extdata;
michael@0 166 // If it exists but is empty then a key was likely deleted. In that case just
michael@0 167 // delete extData.
michael@0 168 if (tabData.extData && !Object.keys(tabData.extData).length)
michael@0 169 delete tabData.extData;
michael@0 170 return tabData;
michael@0 171 }
michael@0 172
michael@0 173 // If there is a userTypedValue set, then either the user has typed something
michael@0 174 // in the URL bar, or a new tab was opened with a URI to load. userTypedClear
michael@0 175 // is used to indicate whether the tab was in some sort of loading state with
michael@0 176 // userTypedValue.
michael@0 177 if (browser.userTypedValue) {
michael@0 178 tabData.userTypedValue = browser.userTypedValue;
michael@0 179 tabData.userTypedClear = browser.userTypedClear;
michael@0 180 } else {
michael@0 181 delete tabData.userTypedValue;
michael@0 182 delete tabData.userTypedClear;
michael@0 183 }
michael@0 184
michael@0 185 if (tab.pinned)
michael@0 186 tabData.pinned = true;
michael@0 187 else
michael@0 188 delete tabData.pinned;
michael@0 189 tabData.hidden = tab.hidden;
michael@0 190
michael@0 191 // Save tab attributes.
michael@0 192 tabData.attributes = TabAttributes.get(tab);
michael@0 193
michael@0 194 // Store the tab icon.
michael@0 195 let tabbrowser = tab.ownerDocument.defaultView.gBrowser;
michael@0 196 tabData.image = tabbrowser.getIcon(tab);
michael@0 197
michael@0 198 if (tab.__SS_extdata)
michael@0 199 tabData.extData = tab.__SS_extdata;
michael@0 200 else if (tabData.extData)
michael@0 201 delete tabData.extData;
michael@0 202
michael@0 203 // Copy data from the tab state cache only if the tab has fully finished
michael@0 204 // restoring. We don't want to overwrite data contained in __SS_data.
michael@0 205 this._copyFromCache(tab, tabData, options);
michael@0 206
michael@0 207 return tabData;
michael@0 208 },
michael@0 209
michael@0 210 /**
michael@0 211 * Copy tab data for the given |tab| from the cache to |tabData|.
michael@0 212 *
michael@0 213 * @param tab (xul:tab)
michael@0 214 * The tab belonging to the given |tabData| object.
michael@0 215 * @param tabData (object)
michael@0 216 * The tab data belonging to the given |tab|.
michael@0 217 * @param options (object)
michael@0 218 * {includePrivateData: true} to always include private data
michael@0 219 */
michael@0 220 _copyFromCache: function (tab, tabData, options = {}) {
michael@0 221 let data = TabStateCache.get(tab.linkedBrowser);
michael@0 222 if (!data) {
michael@0 223 return;
michael@0 224 }
michael@0 225
michael@0 226 // The caller may explicitly request to omit privacy checks.
michael@0 227 let includePrivateData = options && options.includePrivateData;
michael@0 228
michael@0 229 for (let key of Object.keys(data)) {
michael@0 230 let value = data[key];
michael@0 231
michael@0 232 // Filter sensitive data according to the current privacy level.
michael@0 233 if (!includePrivateData) {
michael@0 234 if (key === "storage") {
michael@0 235 value = PrivacyFilter.filterSessionStorageData(value, tab.pinned);
michael@0 236 } else if (key === "formdata") {
michael@0 237 value = PrivacyFilter.filterFormData(value, tab.pinned);
michael@0 238 }
michael@0 239 }
michael@0 240
michael@0 241 if (key === "history") {
michael@0 242 tabData.entries = value.entries;
michael@0 243
michael@0 244 if (value.hasOwnProperty("index")) {
michael@0 245 tabData.index = value.index;
michael@0 246 }
michael@0 247 } else if (value) {
michael@0 248 tabData[key] = value;
michael@0 249 }
michael@0 250 }
michael@0 251 }
michael@0 252 };

mercurial