summaryrefslogtreecommitdiffstats
path: root/toolkit/modules/sessionstore/ScrollPosition.jsm
blob: 5267f332a0535f6b1bee4de64cb3c6008ed91191 (plain)
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
/* 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";

this.EXPORTED_SYMBOLS = ["ScrollPosition"];

const Ci = Components.interfaces;

/**
 * It provides methods to collect scroll positions from single frames and to
 * restore scroll positions for frame trees.
 *
 * This is a child process module.
 */
this.ScrollPosition = Object.freeze({
  collect(frame) {
    return ScrollPositionInternal.collect(frame);
  },

  restoreTree(root, data) {
    ScrollPositionInternal.restoreTree(root, data);
  }
});

/**
 * This module's internal API.
 */
var ScrollPositionInternal = {
  /**
   * Collects scroll position data for any given |frame| in the frame hierarchy.
   *
   * @param frame (DOMWindow)
   *
   * @return {scroll: "x,y"} e.g. {scroll: "100,200"}
   *         Returns null when there is no scroll data we want to store for the
   *         given |frame|.
   */
  collect: function (frame) {
    let ifreq = frame.QueryInterface(Ci.nsIInterfaceRequestor);
    let utils = ifreq.getInterface(Ci.nsIDOMWindowUtils);
    let scrollX = {}, scrollY = {};
    utils.getScrollXY(false /* no layout flush */, scrollX, scrollY);

    if (scrollX.value || scrollY.value) {
      return {scroll: scrollX.value + "," + scrollY.value};
    }

    return null;
  },

  /**
   * Restores scroll position data for any given |frame| in the frame hierarchy.
   *
   * @param frame (DOMWindow)
   * @param value (object, see collect())
   */
  restore: function (frame, value) {
    let match;

    if (value && (match = /(\d+),(\d+)/.exec(value))) {
      frame.scrollTo(match[1], match[2]);
    }
  },

  /**
   * Restores scroll position data for the current frame hierarchy starting at
   * |root| using the given scroll position |data|.
   *
   * If the given |root| frame's hierarchy doesn't match that of the given
   * |data| object we will silently discard data for unreachable frames. We
   * may as well assign scroll positions to the wrong frames if some were
   * reordered or removed.
   *
   * @param root (DOMWindow)
   * @param data (object)
   *        {
   *          scroll: "100,200",
   *          children: [
   *            {scroll: "100,200"},
   *            null,
   *            {scroll: "200,300", children: [ ... ]}
   *          ]
   *        }
   */
  restoreTree: function (root, data) {
    if (data.hasOwnProperty("scroll")) {
      this.restore(root, data.scroll);
    }

    if (!data.hasOwnProperty("children")) {
      return;
    }

    let frames = root.frames;
    data.children.forEach((child, index) => {
      if (child && index < frames.length) {
        this.restoreTree(frames[index], child);
      }
    });
  }
};