summaryrefslogtreecommitdiffstats
path: root/gfx/layers/apz/test/mochitest/test_layerization.html
blob: c74b181bd6eb889006f9716cf21616e082901181 (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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1173580
-->
<head>
  <title>Test for layerization</title>
  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
  <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
  <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
  <script type="application/javascript" src="apz_test_utils.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
  <link rel="stylesheet" type="text/css" href="helper_subframe_style.css"/>
  <style>
  #container {
    display: flex;
    overflow: scroll;
    height: 500px;
  }
  .outer-frame {
    height: 500px;
    overflow: scroll;
    flex-basis: 100%;
    background: repeating-linear-gradient(#CCC, #CCC 100px, #BBB 100px, #BBB 200px);
  }
  #container-content {
    height: 200%;
  }
  </style>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1173580">APZ layerization tests</a>
<p id="display"></p>
<div id="container">
  <div id="outer1" class="outer-frame">
    <div id="inner1" class="inner-frame">
      <div class="inner-content"></div>
    </div>
  </div>
  <div id="outer2" class="outer-frame">
    <div id="inner2" class="inner-frame">
      <div class="inner-content"></div>
    </div>
  </div>
  <iframe id="outer3" class="outer-frame" src="helper_iframe1.html"></iframe>
  <iframe id="outer4" class="outer-frame" src="helper_iframe2.html"></iframe>
<!-- The container-content div ensures 'container' is scrollable, so the
     optimization that layerizes the primary async-scrollable frame on page
     load layerizes it rather than its child subframes. -->
  <div id="container-content"></div>
</div>
<pre id="test">
<script type="application/javascript;version=1.7">

// Scroll the mouse wheel over |element|.
function scrollWheelOver(element, waitForScroll, testDriver) {
  moveMouseAndScrollWheelOver(element, 10, 10, testDriver, waitForScroll);
}

const DISPLAYPORT_EXPIRY = 100;

// This helper function produces another helper function, which, when invoked,
// invokes the provided testDriver argument in a setTimeout 0. This is really
// just useful in cases when there are no paints pending, because then
// waitForAllPaints will invoke its callback synchronously. If we did
// waitForAllPaints(testDriver) that might cause reentrancy into the testDriver
// which is bad. This function works around that.
function asyncWrapper(testDriver) {
  return function() {
    setTimeout(testDriver, 0);
  };
}

function* test(testDriver) {
  // Initially, nothing should be layerized.
  ok(!isLayerized('outer1'), "initially 'outer1' should not be layerized");
  ok(!isLayerized('inner1'), "initially 'inner1' should not be layerized");
  ok(!isLayerized('outer2'), "initially 'outer2' should not be layerized");
  ok(!isLayerized('inner2'), "initially 'inner2' should not be layerized");
  ok(!isLayerized('outer3'), "initially 'outer3' should not be layerized");
  ok(!isLayerized('inner3'), "initially 'inner3' should not be layerized");
  ok(!isLayerized('outer4'), "initially 'outer4' should not be layerized");
  ok(!isLayerized('inner4'), "initially 'inner4' should not be layerized");

  // Scrolling over outer1 should layerize outer1, but not inner1.
  yield scrollWheelOver(document.getElementById('outer1'), true, testDriver);
  ok(isLayerized('outer1'), "scrolling 'outer1' should cause it to be layerized");
  ok(!isLayerized('inner1'), "scrolling 'outer1' should not cause 'inner1' to be layerized");

  // Scrolling over inner2 should layerize both outer2 and inner2.
  yield scrollWheelOver(document.getElementById('inner2'), true, testDriver);
  ok(isLayerized('inner2'), "scrolling 'inner2' should cause it to be layerized");
  ok(isLayerized('outer2'), "scrolling 'inner2' should also cause 'outer2' to be layerized");

  // The second half of the test repeats the same checks as the first half,
  // but with an iframe as the outer scrollable frame.

  // Scrolling over outer3 should layerize outer3, but not inner3.
  yield scrollWheelOver(document.getElementById('outer3').contentDocument.documentElement, true, testDriver);
  ok(isLayerized('outer3'), "scrolling 'outer3' should cause it to be layerized");
  ok(!isLayerized('inner3'), "scrolling 'outer3' should not cause 'inner3' to be layerized");

  // Scrolling over outer4 should layerize both outer4 and inner4.
  yield scrollWheelOver(document.getElementById('outer4').contentDocument.getElementById('inner4'), true, testDriver);
  ok(isLayerized('inner4'), "scrolling 'inner4' should cause it to be layerized");
  ok(isLayerized('outer4'), "scrolling 'inner4' should also cause 'outer4' to be layerized");

  // Now we enable displayport expiry, and verify that things are still
  // layerized as they were before.
  yield SpecialPowers.pushPrefEnv({"set": [["apz.displayport_expiry_ms", DISPLAYPORT_EXPIRY]]}, testDriver);
  ok(isLayerized('outer1'), "outer1 is still layerized after enabling expiry");
  ok(!isLayerized('inner1'), "inner1 is still not layerized after enabling expiry");
  ok(isLayerized('outer2'), "outer2 is still layerized after enabling expiry");
  ok(isLayerized('inner2'), "inner2 is still layerized after enabling expiry");
  ok(isLayerized('outer3'), "outer3 is still layerized after enabling expiry");
  ok(!isLayerized('inner3'), "inner3 is still not layerized after enabling expiry");
  ok(isLayerized('outer4'), "outer4 is still layerized after enabling expiry");
  ok(isLayerized('inner4'), "inner4 is still layerized after enabling expiry");

  // Now we trigger a scroll on some of the things still layerized, so that
  // the displayport expiry gets triggered.

  // Expire displayport with scrolling on outer1
  yield scrollWheelOver(document.getElementById('outer1'), true, testDriver);
  yield waitForAllPaints(function() {
    flushApzRepaints(testDriver);
  });
  yield setTimeout(testDriver, DISPLAYPORT_EXPIRY);
  yield waitForAllPaints(asyncWrapper(testDriver));
  ok(!isLayerized('outer1'), "outer1 is no longer layerized after displayport expiry");
  ok(!isLayerized('inner1'), "inner1 is still not layerized after displayport expiry");

  // Expire displayport with scrolling on inner2
  yield scrollWheelOver(document.getElementById('inner2'), true, testDriver);
  yield waitForAllPaints(function() {
    flushApzRepaints(testDriver);
  });
  // Once the expiry elapses, it will trigger expiry on outer2, so we check
  // both, one at a time.
  yield setTimeout(testDriver, DISPLAYPORT_EXPIRY);
  yield waitForAllPaints(asyncWrapper(testDriver));
  ok(!isLayerized('inner2'), "inner2 is no longer layerized after displayport expiry");
  yield setTimeout(testDriver, DISPLAYPORT_EXPIRY);
  yield waitForAllPaints(asyncWrapper(testDriver));
  ok(!isLayerized('outer2'), "outer2 got de-layerized with inner2");

  // Scroll on inner3. inner3 isn't layerized, and this will cause it to
  // get layerized, but it will also trigger displayport expiration for inner3
  // which will eventually trigger displayport expiration on inner3 and outer3.
  // Note that the displayport expiration might actually happen before the wheel
  // input is processed in the compositor (see bug 1246480 comment 3), and so
  // we make sure not to wait for a scroll event here, since it may never fire.
  // However, if we do get a scroll event while waiting for the expiry, we need
  // to restart the expiry timer because the displayport expiry got reset. There's
  // no good way that I can think of to deterministically avoid doing this.
  let inner3 = document.getElementById('outer3').contentDocument.getElementById('inner3');
  yield scrollWheelOver(inner3, false, testDriver);
  yield waitForAllPaints(function() {
    flushApzRepaints(testDriver);
  });
  var timerId = setTimeout(testDriver, DISPLAYPORT_EXPIRY);
  var timeoutResetter = function() {
    ok(true, "Got a scroll event; resetting timer...");
    clearTimeout(timerId);
    setTimeout(testDriver, DISPLAYPORT_EXPIRY);
    // by not updating timerId we ensure that this listener resets the timeout
    // at most once.
  };
  inner3.addEventListener('scroll', timeoutResetter, false);
  yield; // wait for the setTimeout to elapse
  inner3.removeEventListener('scroll', timeoutResetter, false);

  yield waitForAllPaints(asyncWrapper(testDriver));
  ok(!isLayerized('inner3'), "inner3 becomes unlayerized after expiry");
  yield setTimeout(testDriver, DISPLAYPORT_EXPIRY);
  yield waitForAllPaints(asyncWrapper(testDriver));
  ok(!isLayerized('outer3'), "outer3 is no longer layerized after inner3 triggered expiry");

  // Scroll outer4 and wait for the expiry. It should NOT get expired because
  // inner4 is still layerized
  yield scrollWheelOver(document.getElementById('outer4').contentDocument.documentElement, true, testDriver);
  yield waitForAllPaints(function() {
    flushApzRepaints(testDriver);
  });
  // Wait for the expiry to elapse
  yield setTimeout(testDriver, DISPLAYPORT_EXPIRY);
  yield waitForAllPaints(asyncWrapper(testDriver));
  ok(isLayerized('inner4'), "inner4 is still layerized because it never expired");
  ok(isLayerized('outer4'), "outer4 is still layerized because inner4 is still layerized");
}

if (isApzEnabled()) {
  SimpleTest.waitForExplicitFinish();
  SimpleTest.requestFlakyTimeout("we are testing code that measures an actual timeout");
  SimpleTest.expectAssertions(0, 8); // we get a bunch of "ASSERTION: Bounds computation mismatch" sometimes (bug 1232856)

  // Disable smooth scrolling, because it results in long-running scroll
  // animations that can result in a 'scroll' event triggered by an earlier
  // wheel event as corresponding to a later wheel event.
  // Also enable APZ test logging, since we use that data to determine whether
  // a scroll frame was layerized.
  pushPrefs([["general.smoothScroll", false],
             ["apz.displayport_expiry_ms", 0],
             ["apz.test.logging_enabled", true]])
  .then(waitUntilApzStable)
  .then(runContinuation(test))
  .then(SimpleTest.finish);
}

</script>
</pre>
</body>
</html>