summaryrefslogtreecommitdiffstats
path: root/gfx/layers/apz/test/mochitest/helper_overscroll_in_subscroller.html
blob: 5936de97f70a126db30a5f355c6f74f996b972de (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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
<!DOCTYPE HTML>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, minimum-scale=1.0">
<title>
  Tests that the overscroll gutter in a sub scroll container is restored if it's
  no longer target scroll container
</title>
<script src="apz_test_utils.js"></script>
<script src="apz_test_native_event_utils.js"></script>
<script src="/tests/SimpleTest/paint_listener.js"></script>
<style>
  html {
    overflow: scroll;
  }

  .content {
    height: 500px;
    width: 300px;
    overflow-y: scroll;
    background-color: red;
  }
</style>
<!-- a sub scroll container -->
<div class="content">
  <div style="height:100vh; background-color: white;"></div>
</div>
<div style="height:200vh"></div>
<script>
  document.documentElement.addEventListener(
    "wheel",
    e => {
      if (!e.target.closest(`div[class="content"]`)) {
        e.preventDefault();
      }
    },
    {
      passive: false,
    }
  );

  const subScroller = document.querySelector(`div[class="content"]`);
  // Make the sub scroll container overscrollable at the top edge.
  // A `waitUntilApzStable()` call below ensures that this scroll position
  // has been informed into APZ before starting this test.
  subScroller.scrollTop = 1;

  // A utility function to collect overscrolled scroll container information.
  function collectOverscrolledData() {
    const apzData = SpecialPowers.DOMWindowUtils.getCompositorAPZTestData().additionalData;
    return apzData.filter(data => {
      return SpecialPowers.wrap(data).value.split(",").includes("overscrolled");
    });
  }

  async function test() {
    const oneScrollPromise = new Promise(resolve => {
      subScroller.addEventListener("scroll", () => {
        resolve();
      }, { once: true });
    });

    // Start a pan upward gesture to try oversrolling on the sub scroll
    // container.
    await NativePanHandler.promiseNativePanEvent(
      subScroller,
      100,
      100,
      0,
      -NativePanHandler.delta,
      NativePanHandler.beginPhase
    );

    const rootScrollId =
      SpecialPowers.DOMWindowUtils.getViewId(document.scrollingElement);
    const subScrollId =
      SpecialPowers.DOMWindowUtils.getViewId(subScroller);

    await promiseApzFlushedRepaints();
    await oneScrollPromise;

    let overscrolledData = collectOverscrolledData();
    ok(overscrolledData.length >= 1,
       "There should be at least one overscrolled scroll container");
    ok(overscrolledData.every(data => SpecialPowers.wrap(data).key == subScrollId),
       "The overscrolled scroll container should be the sub scroll container");

    let twoScrollEndPromise = new Promise(resolve => {
      let count = 0;
      subScroller.addEventListener("scrollend", () => {
        count++;
        ok(count <= 2, "There should never be more than two scrollend events");
        if (count == 2) {
          resolve();
        }
      });
    });

    // Finish the pan upward gesture.
    await NativePanHandler.promiseNativePanEvent(
      subScroller,
      100,
      100,
      0,
      0,
      NativePanHandler.endPhase
    );

    await promiseApzFlushedRepaints();

    // Now do another pan upward gesture again.
    await NativePanHandler.promiseNativePanEvent(
      subScroller,
      100,
      100,
      0,
      -NativePanHandler.delta,
      NativePanHandler.beginPhase
    );

    // Wait two `apz-repaints-flushed`s to give a chance to overscroll the root
    // scroll container.
    await promiseApzFlushedRepaints();
    await promiseApzFlushedRepaints();

    overscrolledData = collectOverscrolledData();
    ok(overscrolledData.length >= 2,
       "There should be at least two overscrolled scroll containers");
    ok(overscrolledData.some(data => SpecialPowers.wrap(data).key == rootScrollId),
       "The root scroll container should be overscrolled");
    ok(overscrolledData.some(data => SpecialPowers.wrap(data).key == subScrollId),
       "The sub scroll container should also be overscrolled");

    // While the root scroll container is still being overscrolled because the
    // new pan gesture is still on-going, the sub scroll container should be
    // restored.
    // Note that this test relies on the fact that two scrollend events get
    // fired when overscrolling happens, one gets fired when the scroll position
    // reached to the edge of the scrollport (i.e. just about to start
    // overscrolling), the other one gets fired when overscrolling ends.
    await twoScrollEndPromise;
    info("Got two scroll end events on the sub scroll container");

    await promiseApzFlushedRepaints();

    overscrolledData = collectOverscrolledData();
    ok(overscrolledData.length >= 1,
       "There should be at least one overscrolled scroll container");
    ok(overscrolledData.every(data => SpecialPowers.wrap(data).key == rootScrollId),
       "The root scroll container should still be overscrolled");

    // Finish the pan upward gesture.
    await NativePanHandler.promiseNativePanEvent(
      subScroller,
      100,
      100,
      0,
      0,
      NativePanHandler.endPhase
    );
  }

  waitUntilApzStable()
  .then(test)
  .then(subtestDone, subtestFailed);
</script>