Bug 1456391 - Part 2: Add mapFrameTree to sessionstore's Utils.jsm. r?mikedeboer
GeckoView has already started using a slightly modified version of mapFrameTree,
and since ssu.forEachNonDynamicChildFrame() has vastly simplified the process of
correctly using FormData/ScrollPosition.collect() for *all* (non-dynamic) child
frames, we want to use mapFrameTree for Fennec's session store as well.
Therefore, to avoid further duplication of code, we add a common version to the
session store's Utils.jsm module.
We base the code on the GeckoView implementation of mapFrameTree, which has
gained the ability to use callback *arrays*, however we still use ssu.forEach-
NonDynamicChildFrame() like Desktop currently does, instead of simply iterating
over *all* frames.
MozReview-Commit-ID: 3ilEgNSeCEv
--- a/toolkit/modules/sessionstore/Utils.jsm
+++ b/toolkit/modules/sessionstore/Utils.jsm
@@ -9,16 +9,19 @@ var EXPORTED_SYMBOLS = ["Utils"];
ChromeUtils.import("resource://gre/modules/Services.jsm", this);
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", this);
ChromeUtils.defineModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "serializationHelper",
"@mozilla.org/network/serialization-helper;1",
"nsISerializationHelper");
+XPCOMUtils.defineLazyServiceGetter(this, "ssu",
+ "@mozilla.org/browser/sessionstore/utils;1",
+ "nsISessionStoreUtils");
XPCOMUtils.defineLazyGetter(this, "SERIALIZED_SYSTEMPRINCIPAL", function() {
return Utils.serializePrincipal(Services.scriptSecurityManager.getSystemPrincipal());
});
function debug(msg) {
Services.console.logStringMessage("Utils: " + msg);
}
@@ -142,10 +145,56 @@ var Utils = Object.freeze({
try {
let principal = serializationHelper.deserializeObject(principal_b64);
principal.QueryInterface(Ci.nsIPrincipal);
return principal;
} catch (e) {
debug(`Failed to deserialize principal_b64 '${principal_b64}' ${e}`);
}
return null;
+ },
+
+ /**
+ * A function that will recursively call |cb| to collect data for all
+ * non-dynamic frames in the current frame/docShell tree.
+ *
+ * @param {mozIDOMWindowProxy} frame A DOM window or content frame for which
+ * data will be collected.
+ * @param {...function} dataCollectors One or more data collection functions
+ * that will be called once for each non-
+ * dynamic frame in the given frame tree,
+ * and which should return the data they
+ * wish to save for that respective frame.
+ * @return {object[]} An array with one entry per dataCollector, containing
+ * the collected data as a nested data structure according
+ * to the layout of the frame tree, or null if no data was
+ * returned by the respective dataCollector.
+ */
+ mapFrameTree(frame, ...dataCollectors) {
+ // Collect data for the current frame.
+ let objs = dataCollectors.map((dataCollector) => dataCollector(frame) || {});
+ let children = dataCollectors.map(() => []);
+
+ // Recurse into child frames.
+ ssu.forEachNonDynamicChildFrame(frame, (subframe, index) => {
+ let results = this.mapFrameTree(subframe, ...dataCollectors);
+ if (!results) {
+ return;
+ }
+
+ for (let j = results.length - 1; j >= 0; --j) {
+ if (!results[j] || !Object.getOwnPropertyNames(results[j]).length) {
+ continue;
+ }
+ children[j][index] = results[j];
+ }
+ });
+
+ for (let i = objs.length - 1; i >= 0; --i) {
+ if (!children[i].length) {
+ continue;
+ }
+ objs[i].children = children[i];
+ }
+
+ return objs.map((obj) => Object.getOwnPropertyNames(obj).length ? obj : null);
}
});