michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: this.EXPORTED_SYMBOLS = ["ScrollPosition"]; michael@0: michael@0: const Ci = Components.interfaces; michael@0: michael@0: /** michael@0: * It provides methods to collect and restore scroll positions for single michael@0: * frames and frame trees. michael@0: * michael@0: * This is a child process module. michael@0: */ michael@0: this.ScrollPosition = Object.freeze({ michael@0: /** michael@0: * Collects scroll position data for any given |frame| in the frame hierarchy. michael@0: * michael@0: * @param frame (DOMWindow) michael@0: * michael@0: * @return {scroll: "x,y"} e.g. {scroll: "100,200"} michael@0: * Returns null when there is no scroll data we want to store for the michael@0: * given |frame|. michael@0: */ michael@0: collect: function (frame) { michael@0: let ifreq = frame.QueryInterface(Ci.nsIInterfaceRequestor); michael@0: let utils = ifreq.getInterface(Ci.nsIDOMWindowUtils); michael@0: let scrollX = {}, scrollY = {}; michael@0: utils.getScrollXY(false /* no layout flush */, scrollX, scrollY); michael@0: michael@0: if (scrollX.value || scrollY.value) { michael@0: return {scroll: scrollX.value + "," + scrollY.value}; michael@0: } michael@0: michael@0: return null; michael@0: }, michael@0: michael@0: /** michael@0: * Restores scroll position data for any given |frame| in the frame hierarchy. michael@0: * michael@0: * @param frame (DOMWindow) michael@0: * @param value (object, see collect()) michael@0: */ michael@0: restore: function (frame, value) { michael@0: let match; michael@0: michael@0: if (value && (match = /(\d+),(\d+)/.exec(value))) { michael@0: frame.scrollTo(match[1], match[2]); michael@0: } michael@0: }, michael@0: michael@0: /** michael@0: * Restores scroll position data for the current frame hierarchy starting at michael@0: * |root| using the given scroll position |data|. michael@0: * michael@0: * If the given |root| frame's hierarchy doesn't match that of the given michael@0: * |data| object we will silently discard data for unreachable frames. We michael@0: * may as well assign scroll positions to the wrong frames if some were michael@0: * reordered or removed. michael@0: * michael@0: * @param root (DOMWindow) michael@0: * @param data (object) michael@0: * { michael@0: * scroll: "100,200", michael@0: * children: [ michael@0: * {scroll: "100,200"}, michael@0: * null, michael@0: * {scroll: "200,300", children: [ ... ]} michael@0: * ] michael@0: * } michael@0: */ michael@0: restoreTree: function (root, data) { michael@0: if (data.hasOwnProperty("scroll")) { michael@0: this.restore(root, data.scroll); michael@0: } michael@0: michael@0: if (!data.hasOwnProperty("children")) { michael@0: return; michael@0: } michael@0: michael@0: let frames = root.frames; michael@0: data.children.forEach((child, index) => { michael@0: if (child && index < frames.length) { michael@0: this.restoreTree(frames[index], child); michael@0: } michael@0: }); michael@0: } michael@0: });