Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/layers/apz/test/gtest/TestHitTesting.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "APZCTreeManagerTester.h"
8
#include "APZTestCommon.h"
9
#include "gfxPrefs.h"
10
#include "InputUtils.h"
11
12
class APZHitTestingTester : public APZCTreeManagerTester {
13
protected:
14
  ScreenToParentLayerMatrix4x4 transformToApzc;
15
  ParentLayerToScreenMatrix4x4 transformToGecko;
16
17
0
  already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScreenPoint& aPoint) {
18
0
    RefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(aPoint, nullptr);
19
0
    if (hit) {
20
0
      transformToApzc = manager->GetScreenToApzcTransform(hit.get());
21
0
      transformToGecko = manager->GetApzcToGeckoTransform(hit.get());
22
0
    }
23
0
    return hit.forget();
24
0
  }
25
26
protected:
27
0
  void CreateHitTesting1LayerTree() {
28
0
    const char* layerTreeSyntax = "c(tttt)";
29
0
    // LayerID                     0 1234
30
0
    nsIntRegion layerVisibleRegion[] = {
31
0
      nsIntRegion(IntRect(0,0,100,100)),
32
0
      nsIntRegion(IntRect(0,0,100,100)),
33
0
      nsIntRegion(IntRect(10,10,20,20)),
34
0
      nsIntRegion(IntRect(10,10,20,20)),
35
0
      nsIntRegion(IntRect(5,5,20,20)),
36
0
    };
37
0
    root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
38
0
  }
39
40
0
  void CreateHitTesting2LayerTree() {
41
0
    const char* layerTreeSyntax = "c(tc(t))";
42
0
    // LayerID                     0 12 3
43
0
    nsIntRegion layerVisibleRegion[] = {
44
0
      nsIntRegion(IntRect(0,0,100,100)),
45
0
      nsIntRegion(IntRect(10,10,40,40)),
46
0
      nsIntRegion(IntRect(10,60,40,40)),
47
0
      nsIntRegion(IntRect(10,60,40,40)),
48
0
    };
49
0
    Matrix4x4 transforms[] = {
50
0
      Matrix4x4(),
51
0
      Matrix4x4(),
52
0
      Matrix4x4::Scaling(2, 1, 1),
53
0
      Matrix4x4(),
54
0
    };
55
0
    root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, lm, layers);
56
0
57
0
    SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200));
58
0
    SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 80, 80));
59
0
    SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 2, CSSRect(0, 0, 80, 80));
60
0
  }
61
62
0
  void DisableApzOn(Layer* aLayer) {
63
0
    ScrollMetadata m = aLayer->GetScrollMetadata(0);
64
0
    m.SetForceDisableApz(true);
65
0
    aLayer->SetScrollMetadata(m);
66
0
  }
67
68
0
  void CreateComplexMultiLayerTree() {
69
0
    const char* layerTreeSyntax = "c(tc(t)tc(c(t)tt))";
70
0
    // LayerID                     0 12 3 45 6 7 89
71
0
    nsIntRegion layerVisibleRegion[] = {
72
0
      nsIntRegion(IntRect(0,0,300,400)),      // root(0)
73
0
      nsIntRegion(IntRect(0,0,100,100)),      // thebes(1) in top-left
74
0
      nsIntRegion(IntRect(50,50,200,300)),    // container(2) centered in root(0)
75
0
      nsIntRegion(IntRect(50,50,200,300)),    // thebes(3) fully occupying parent container(2)
76
0
      nsIntRegion(IntRect(0,200,100,100)),    // thebes(4) in bottom-left
77
0
      nsIntRegion(IntRect(200,0,100,400)),    // container(5) along the right 100px of root(0)
78
0
      nsIntRegion(IntRect(200,0,100,200)),    // container(6) taking up the top half of parent container(5)
79
0
      nsIntRegion(IntRect(200,0,100,200)),    // thebes(7) fully occupying parent container(6)
80
0
      nsIntRegion(IntRect(200,200,100,100)),  // thebes(8) in bottom-right (below (6))
81
0
      nsIntRegion(IntRect(200,300,100,100)),  // thebes(9) in bottom-right (below (8))
82
0
    };
83
0
    root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
84
0
    SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID);
85
0
    SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID);
86
0
    SetScrollableFrameMetrics(layers[4], FrameMetrics::START_SCROLL_ID + 1);
87
0
    SetScrollableFrameMetrics(layers[6], FrameMetrics::START_SCROLL_ID + 1);
88
0
    SetScrollableFrameMetrics(layers[7], FrameMetrics::START_SCROLL_ID + 2);
89
0
    SetScrollableFrameMetrics(layers[8], FrameMetrics::START_SCROLL_ID + 1);
90
0
    SetScrollableFrameMetrics(layers[9], FrameMetrics::START_SCROLL_ID + 3);
91
0
  }
92
93
0
  void CreateBug1148350LayerTree() {
94
0
    const char* layerTreeSyntax = "c(t)";
95
0
    // LayerID                     0 1
96
0
    nsIntRegion layerVisibleRegion[] = {
97
0
      nsIntRegion(IntRect(0,0,200,200)),
98
0
      nsIntRegion(IntRect(0,0,200,200)),
99
0
    };
100
0
    root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
101
0
    SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID);
102
0
  }
103
};
104
105
// A simple hit testing test that doesn't involve any transforms on layers.
106
0
TEST_F(APZHitTestingTester, HitTesting1) {
107
0
  SCOPED_GFX_VAR(UseWebRender, bool, false);
108
0
109
0
  CreateHitTesting1LayerTree();
110
0
  ScopedLayerTreeRegistration registration(manager, LayersId{0}, root, mcc);
111
0
112
0
  // No APZC attached so hit testing will return no APZC at (20,20)
113
0
  RefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(20, 20));
114
0
  TestAsyncPanZoomController* nullAPZC = nullptr;
115
0
  EXPECT_EQ(nullAPZC, hit.get());
116
0
  EXPECT_EQ(ScreenToParentLayerMatrix4x4(), transformToApzc);
117
0
  EXPECT_EQ(ParentLayerToScreenMatrix4x4(), transformToGecko);
118
0
119
0
  uint32_t paintSequenceNumber = 0;
120
0
121
0
  // Now we have a root APZC that will match the page
122
0
  SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID);
123
0
  manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, paintSequenceNumber++);
124
0
  hit = GetTargetAPZC(ScreenPoint(15, 15));
125
0
  EXPECT_EQ(ApzcOf(root), hit.get());
126
0
  // expect hit point at LayerIntPoint(15, 15)
127
0
  EXPECT_EQ(ParentLayerPoint(15, 15), transformToApzc.TransformPoint(ScreenPoint(15, 15)));
128
0
  EXPECT_EQ(ScreenPoint(15, 15), transformToGecko.TransformPoint(ParentLayerPoint(15, 15)));
129
0
130
0
  // Now we have a sub APZC with a better fit
131
0
  SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 1);
132
0
  manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, paintSequenceNumber++);
133
0
  EXPECT_NE(ApzcOf(root), ApzcOf(layers[3]));
134
0
  hit = GetTargetAPZC(ScreenPoint(25, 25));
135
0
  EXPECT_EQ(ApzcOf(layers[3]), hit.get());
136
0
  // expect hit point at LayerIntPoint(25, 25)
137
0
  EXPECT_EQ(ParentLayerPoint(25, 25), transformToApzc.TransformPoint(ScreenPoint(25, 25)));
138
0
  EXPECT_EQ(ScreenPoint(25, 25), transformToGecko.TransformPoint(ParentLayerPoint(25, 25)));
139
0
140
0
  // At this point, layers[4] obscures layers[3] at the point (15, 15) so
141
0
  // hitting there should hit the root APZC
142
0
  hit = GetTargetAPZC(ScreenPoint(15, 15));
143
0
  EXPECT_EQ(ApzcOf(root), hit.get());
144
0
145
0
  // Now test hit testing when we have two scrollable layers
146
0
  SetScrollableFrameMetrics(layers[4], FrameMetrics::START_SCROLL_ID + 2);
147
0
  manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, paintSequenceNumber++);
148
0
  hit = GetTargetAPZC(ScreenPoint(15, 15));
149
0
  EXPECT_EQ(ApzcOf(layers[4]), hit.get());
150
0
  // expect hit point at LayerIntPoint(15, 15)
151
0
  EXPECT_EQ(ParentLayerPoint(15, 15), transformToApzc.TransformPoint(ScreenPoint(15, 15)));
152
0
  EXPECT_EQ(ScreenPoint(15, 15), transformToGecko.TransformPoint(ParentLayerPoint(15, 15)));
153
0
154
0
  // Hit test ouside the reach of layer[3,4] but inside root
155
0
  hit = GetTargetAPZC(ScreenPoint(90, 90));
156
0
  EXPECT_EQ(ApzcOf(root), hit.get());
157
0
  // expect hit point at LayerIntPoint(90, 90)
158
0
  EXPECT_EQ(ParentLayerPoint(90, 90), transformToApzc.TransformPoint(ScreenPoint(90, 90)));
159
0
  EXPECT_EQ(ScreenPoint(90, 90), transformToGecko.TransformPoint(ParentLayerPoint(90, 90)));
160
0
161
0
  // Hit test ouside the reach of any layer
162
0
  hit = GetTargetAPZC(ScreenPoint(1000, 10));
163
0
  EXPECT_EQ(nullAPZC, hit.get());
164
0
  EXPECT_EQ(ScreenToParentLayerMatrix4x4(), transformToApzc);
165
0
  EXPECT_EQ(ParentLayerToScreenMatrix4x4(), transformToGecko);
166
0
  hit = GetTargetAPZC(ScreenPoint(-1000, 10));
167
0
  EXPECT_EQ(nullAPZC, hit.get());
168
0
  EXPECT_EQ(ScreenToParentLayerMatrix4x4(), transformToApzc);
169
0
  EXPECT_EQ(ParentLayerToScreenMatrix4x4(), transformToGecko);
170
0
}
171
172
// A more involved hit testing test that involves css and async transforms.
173
0
TEST_F(APZHitTestingTester, HitTesting2) {
174
0
  SCOPED_GFX_VAR(UseWebRender, bool, false);
175
0
  SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests
176
0
177
0
  CreateHitTesting2LayerTree();
178
0
  ScopedLayerTreeRegistration registration(manager, LayersId{0}, root, mcc);
179
0
180
0
  manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0);
181
0
182
0
  // At this point, the following holds (all coordinates in screen pixels):
183
0
  // layers[0] has content from (0,0)-(200,200), clipped by composition bounds (0,0)-(100,100)
184
0
  // layers[1] has content from (10,10)-(90,90), clipped by composition bounds (10,10)-(50,50)
185
0
  // layers[2] has content from (20,60)-(100,100). no clipping as it's not a scrollable layer
186
0
  // layers[3] has content from (20,60)-(180,140), clipped by composition bounds (20,60)-(100,100)
187
0
188
0
  RefPtr<TestAsyncPanZoomController> apzcroot = ApzcOf(root);
189
0
  TestAsyncPanZoomController* apzc1 = ApzcOf(layers[1]);
190
0
  TestAsyncPanZoomController* apzc3 = ApzcOf(layers[3]);
191
0
192
0
  // Hit an area that's clearly on the root layer but not any of the child layers.
193
0
  RefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(75, 25));
194
0
  EXPECT_EQ(apzcroot, hit.get());
195
0
  EXPECT_EQ(ParentLayerPoint(75, 25), transformToApzc.TransformPoint(ScreenPoint(75, 25)));
196
0
  EXPECT_EQ(ScreenPoint(75, 25), transformToGecko.TransformPoint(ParentLayerPoint(75, 25)));
197
0
198
0
  // Hit an area on the root that would be on layers[3] if layers[2]
199
0
  // weren't transformed.
200
0
  // Note that if layers[2] were scrollable, then this would hit layers[2]
201
0
  // because its composition bounds would be at (10,60)-(50,100) (and the
202
0
  // scale-only transform that we set on layers[2] would be invalid because
203
0
  // it would place the layer into overscroll, as its composition bounds
204
0
  // start at x=10 but its content at x=20).
205
0
  hit = GetTargetAPZC(ScreenPoint(15, 75));
206
0
  EXPECT_EQ(apzcroot, hit.get());
207
0
  EXPECT_EQ(ParentLayerPoint(15, 75), transformToApzc.TransformPoint(ScreenPoint(15, 75)));
208
0
  EXPECT_EQ(ScreenPoint(15, 75), transformToGecko.TransformPoint(ParentLayerPoint(15, 75)));
209
0
210
0
  // Hit an area on layers[1].
211
0
  hit = GetTargetAPZC(ScreenPoint(25, 25));
212
0
  EXPECT_EQ(apzc1, hit.get());
213
0
  EXPECT_EQ(ParentLayerPoint(25, 25), transformToApzc.TransformPoint(ScreenPoint(25, 25)));
214
0
  EXPECT_EQ(ScreenPoint(25, 25), transformToGecko.TransformPoint(ParentLayerPoint(25, 25)));
215
0
216
0
  // Hit an area on layers[3].
217
0
  hit = GetTargetAPZC(ScreenPoint(25, 75));
218
0
  EXPECT_EQ(apzc3, hit.get());
219
0
  // transformToApzc should unapply layers[2]'s transform
220
0
  EXPECT_EQ(ParentLayerPoint(12.5, 75), transformToApzc.TransformPoint(ScreenPoint(25, 75)));
221
0
  // and transformToGecko should reapply it
222
0
  EXPECT_EQ(ScreenPoint(25, 75), transformToGecko.TransformPoint(ParentLayerPoint(12.5, 75)));
223
0
224
0
  // Hit an area on layers[3] that would be on the root if layers[2]
225
0
  // weren't transformed.
226
0
  hit = GetTargetAPZC(ScreenPoint(75, 75));
227
0
  EXPECT_EQ(apzc3, hit.get());
228
0
  // transformToApzc should unapply layers[2]'s transform
229
0
  EXPECT_EQ(ParentLayerPoint(37.5, 75), transformToApzc.TransformPoint(ScreenPoint(75, 75)));
230
0
  // and transformToGecko should reapply it
231
0
  EXPECT_EQ(ScreenPoint(75, 75), transformToGecko.TransformPoint(ParentLayerPoint(37.5, 75)));
232
0
233
0
  // Pan the root layer upward by 50 pixels.
234
0
  // This causes layers[1] to scroll out of view, and an async transform
235
0
  // of -50 to be set on the root layer.
236
0
  EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
237
0
238
0
  // This first pan will move the APZC by 50 pixels, and dispatch a paint request.
239
0
  // Since this paint request is in the queue to Gecko, transformToGecko will
240
0
  // take it into account.
241
0
  Pan(apzcroot, 100, 50, PanOptions::NoFling);
242
0
243
0
  // Hit where layers[3] used to be. It should now hit the root.
244
0
  hit = GetTargetAPZC(ScreenPoint(75, 75));
245
0
  EXPECT_EQ(apzcroot, hit.get());
246
0
  // transformToApzc doesn't unapply the root's own async transform
247
0
  EXPECT_EQ(ParentLayerPoint(75, 75), transformToApzc.TransformPoint(ScreenPoint(75, 75)));
248
0
  // and transformToGecko unapplies it and then reapplies it, because by the
249
0
  // time the event being transformed reaches Gecko the new paint request will
250
0
  // have been handled.
251
0
  EXPECT_EQ(ScreenPoint(75, 75), transformToGecko.TransformPoint(ParentLayerPoint(75, 75)));
252
0
253
0
  // Hit where layers[1] used to be and where layers[3] should now be.
254
0
  hit = GetTargetAPZC(ScreenPoint(25, 25));
255
0
  EXPECT_EQ(apzc3, hit.get());
256
0
  // transformToApzc unapplies both layers[2]'s css transform and the root's
257
0
  // async transform
258
0
  EXPECT_EQ(ParentLayerPoint(12.5, 75), transformToApzc.TransformPoint(ScreenPoint(25, 25)));
259
0
  // transformToGecko reapplies both the css transform and the async transform
260
0
  // because we have already issued a paint request with it.
261
0
  EXPECT_EQ(ScreenPoint(25, 25), transformToGecko.TransformPoint(ParentLayerPoint(12.5, 75)));
262
0
263
0
  // This second pan will move the APZC by another 50 pixels.
264
0
  EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
265
0
  Pan(apzcroot, 100, 50, PanOptions::NoFling);
266
0
267
0
  // Hit where layers[3] used to be. It should now hit the root.
268
0
  hit = GetTargetAPZC(ScreenPoint(75, 75));
269
0
  EXPECT_EQ(apzcroot, hit.get());
270
0
  // transformToApzc doesn't unapply the root's own async transform
271
0
  EXPECT_EQ(ParentLayerPoint(75, 75), transformToApzc.TransformPoint(ScreenPoint(75, 75)));
272
0
  // transformToGecko unapplies the full async transform of -100 pixels
273
0
  EXPECT_EQ(ScreenPoint(75, 75), transformToGecko.TransformPoint(ParentLayerPoint(75, 75)));
274
0
275
0
  // Hit where layers[1] used to be. It should now hit the root.
276
0
  hit = GetTargetAPZC(ScreenPoint(25, 25));
277
0
  EXPECT_EQ(apzcroot, hit.get());
278
0
  // transformToApzc doesn't unapply the root's own async transform
279
0
  EXPECT_EQ(ParentLayerPoint(25, 25), transformToApzc.TransformPoint(ScreenPoint(25, 25)));
280
0
  // transformToGecko unapplies the full async transform of -100 pixels
281
0
  EXPECT_EQ(ScreenPoint(25, 25), transformToGecko.TransformPoint(ParentLayerPoint(25, 25)));
282
0
}
283
284
0
TEST_F(APZHitTestingTester, HitTesting3) {
285
0
  SCOPED_GFX_VAR(UseWebRender, bool, false);
286
0
287
0
  const char* layerTreeSyntax = "c(t)";
288
0
  // LayerID                     0 1
289
0
  nsIntRegion layerVisibleRegions[] = {
290
0
      nsIntRegion(IntRect(0,0,200,200)),
291
0
      nsIntRegion(IntRect(0,0,50,50))
292
0
  };
293
0
  Matrix4x4 transforms[] = {
294
0
      Matrix4x4(),
295
0
      Matrix4x4::Scaling(2, 2, 1)
296
0
  };
297
0
  root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, transforms, lm, layers);
298
0
  // No actual room to scroll
299
0
  SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200));
300
0
  SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 50, 50));
301
0
302
0
  ScopedLayerTreeRegistration registration(manager, LayersId{0}, root, mcc);
303
0
304
0
  manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0);
305
0
306
0
  RefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(75, 75));
307
0
  EXPECT_EQ(ApzcOf(layers[1]), hit.get());
308
0
}
309
310
0
TEST_F(APZHitTestingTester, ComplexMultiLayerTree) {
311
0
  SCOPED_GFX_VAR(UseWebRender, bool, false);
312
0
313
0
  CreateComplexMultiLayerTree();
314
0
  ScopedLayerTreeRegistration registration(manager, LayersId{0}, root, mcc);
315
0
  manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0);
316
0
317
0
  /* The layer tree looks like this:
318
0
319
0
                0
320
0
        |----|--+--|----|
321
0
        1    2     4    5
322
0
             |         /|\
323
0
             3        6 8 9
324
0
                      |
325
0
                      7
326
0
327
0
     Layers 1,2 have the same APZC
328
0
     Layers 4,6,8 have the same APZC
329
0
     Layer 7 has an APZC
330
0
     Layer 9 has an APZC
331
0
  */
332
0
333
0
  TestAsyncPanZoomController* nullAPZC = nullptr;
334
0
  // Ensure all the scrollable layers have an APZC
335
0
  EXPECT_FALSE(layers[0]->HasScrollableFrameMetrics());
336
0
  EXPECT_NE(nullAPZC, ApzcOf(layers[1]));
337
0
  EXPECT_NE(nullAPZC, ApzcOf(layers[2]));
338
0
  EXPECT_FALSE(layers[3]->HasScrollableFrameMetrics());
339
0
  EXPECT_NE(nullAPZC, ApzcOf(layers[4]));
340
0
  EXPECT_FALSE(layers[5]->HasScrollableFrameMetrics());
341
0
  EXPECT_NE(nullAPZC, ApzcOf(layers[6]));
342
0
  EXPECT_NE(nullAPZC, ApzcOf(layers[7]));
343
0
  EXPECT_NE(nullAPZC, ApzcOf(layers[8]));
344
0
  EXPECT_NE(nullAPZC, ApzcOf(layers[9]));
345
0
  // Ensure those that scroll together have the same APZCs
346
0
  EXPECT_EQ(ApzcOf(layers[1]), ApzcOf(layers[2]));
347
0
  EXPECT_EQ(ApzcOf(layers[4]), ApzcOf(layers[6]));
348
0
  EXPECT_EQ(ApzcOf(layers[8]), ApzcOf(layers[6]));
349
0
  // Ensure those that don't scroll together have different APZCs
350
0
  EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[4]));
351
0
  EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[7]));
352
0
  EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[9]));
353
0
  EXPECT_NE(ApzcOf(layers[4]), ApzcOf(layers[7]));
354
0
  EXPECT_NE(ApzcOf(layers[4]), ApzcOf(layers[9]));
355
0
  EXPECT_NE(ApzcOf(layers[7]), ApzcOf(layers[9]));
356
0
  // Ensure the APZC parent chains are set up correctly
357
0
  TestAsyncPanZoomController* layers1_2 = ApzcOf(layers[1]);
358
0
  TestAsyncPanZoomController* layers4_6_8 = ApzcOf(layers[4]);
359
0
  TestAsyncPanZoomController* layer7 = ApzcOf(layers[7]);
360
0
  TestAsyncPanZoomController* layer9 = ApzcOf(layers[9]);
361
0
  EXPECT_EQ(nullptr, layers1_2->GetParent());
362
0
  EXPECT_EQ(nullptr, layers4_6_8->GetParent());
363
0
  EXPECT_EQ(layers4_6_8, layer7->GetParent());
364
0
  EXPECT_EQ(nullptr, layer9->GetParent());
365
0
  // Ensure the hit-testing tree looks like the layer tree
366
0
  RefPtr<HitTestingTreeNode> root = manager->GetRootNode();
367
0
  RefPtr<HitTestingTreeNode> node5 = root->GetLastChild();
368
0
  RefPtr<HitTestingTreeNode> node4 = node5->GetPrevSibling();
369
0
  RefPtr<HitTestingTreeNode> node2 = node4->GetPrevSibling();
370
0
  RefPtr<HitTestingTreeNode> node1 = node2->GetPrevSibling();
371
0
  RefPtr<HitTestingTreeNode> node3 = node2->GetLastChild();
372
0
  RefPtr<HitTestingTreeNode> node9 = node5->GetLastChild();
373
0
  RefPtr<HitTestingTreeNode> node8 = node9->GetPrevSibling();
374
0
  RefPtr<HitTestingTreeNode> node6 = node8->GetPrevSibling();
375
0
  RefPtr<HitTestingTreeNode> node7 = node6->GetLastChild();
376
0
  EXPECT_EQ(nullptr, node1->GetPrevSibling());
377
0
  EXPECT_EQ(nullptr, node3->GetPrevSibling());
378
0
  EXPECT_EQ(nullptr, node6->GetPrevSibling());
379
0
  EXPECT_EQ(nullptr, node7->GetPrevSibling());
380
0
  EXPECT_EQ(nullptr, node1->GetLastChild());
381
0
  EXPECT_EQ(nullptr, node3->GetLastChild());
382
0
  EXPECT_EQ(nullptr, node4->GetLastChild());
383
0
  EXPECT_EQ(nullptr, node7->GetLastChild());
384
0
  EXPECT_EQ(nullptr, node8->GetLastChild());
385
0
  EXPECT_EQ(nullptr, node9->GetLastChild());
386
0
387
0
  RefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(25, 25));
388
0
  EXPECT_EQ(ApzcOf(layers[1]), hit.get());
389
0
  hit = GetTargetAPZC(ScreenPoint(275, 375));
390
0
  EXPECT_EQ(ApzcOf(layers[9]), hit.get());
391
0
  hit = GetTargetAPZC(ScreenPoint(250, 100));
392
0
  EXPECT_EQ(ApzcOf(layers[7]), hit.get());
393
0
}
394
395
0
TEST_F(APZHitTestingTester, TestRepaintFlushOnNewInputBlock) {
396
0
  SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
397
0
398
0
  // The main purpose of this test is to verify that touch-start events (or anything
399
0
  // that starts a new input block) don't ever get untransformed. This should always
400
0
  // hold because the APZ code should flush repaints when we start a new input block
401
0
  // and the transform to gecko space should be empty.
402
0
403
0
  CreateSimpleScrollingLayer();
404
0
  ScopedLayerTreeRegistration registration(manager, LayersId{0}, root, mcc);
405
0
  manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0);
406
0
  RefPtr<TestAsyncPanZoomController> apzcroot = ApzcOf(root);
407
0
408
0
  // At this point, the following holds (all coordinates in screen pixels):
409
0
  // layers[0] has content from (0,0)-(500,500), clipped by composition bounds (0,0)-(200,200)
410
0
411
0
  MockFunction<void(std::string checkPointName)> check;
412
0
413
0
  {
414
0
    InSequence s;
415
0
416
0
    EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1));
417
0
    EXPECT_CALL(check, Call("post-first-touch-start"));
418
0
    EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1));
419
0
    EXPECT_CALL(check, Call("post-second-fling"));
420
0
    EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1));
421
0
    EXPECT_CALL(check, Call("post-second-touch-start"));
422
0
  }
423
0
424
0
  // This first pan will move the APZC by 50 pixels, and dispatch a paint request.
425
0
  Pan(apzcroot, 100, 50, PanOptions::NoFling);
426
0
427
0
  // Verify that a touch start doesn't get untransformed
428
0
  ScreenIntPoint touchPoint(50, 50);
429
0
  MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
430
0
  mti.mTouches.AppendElement(SingleTouchData(0, touchPoint, ScreenSize(0, 0), 0, 0));
431
0
432
0
  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr, nullptr));
433
0
  EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
434
0
  check.Call("post-first-touch-start");
435
0
436
0
  // Send a touchend to clear state
437
0
  mti.mType = MultiTouchInput::MULTITOUCH_END;
438
0
  manager->ReceiveInputEvent(mti, nullptr, nullptr);
439
0
440
0
  mcc->AdvanceByMillis(1000);
441
0
442
0
  // Now do two pans. The first of these will dispatch a repaint request, as above.
443
0
  // The second will get stuck in the paint throttler because the first one doesn't
444
0
  // get marked as "completed", so this will result in a non-empty LD transform.
445
0
  // (Note that any outstanding repaint requests from the first half of this test
446
0
  // don't impact this half because we advance the time by 1 second, which will trigger
447
0
  // the max-wait-exceeded codepath in the paint throttler).
448
0
  Pan(apzcroot, 100, 50, PanOptions::NoFling);
449
0
  check.Call("post-second-fling");
450
0
  Pan(apzcroot, 100, 50, PanOptions::NoFling);
451
0
452
0
  // Ensure that a touch start again doesn't get untransformed by flushing
453
0
  // a repaint
454
0
  mti.mType = MultiTouchInput::MULTITOUCH_START;
455
0
  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr, nullptr));
456
0
  EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
457
0
  check.Call("post-second-touch-start");
458
0
459
0
  mti.mType = MultiTouchInput::MULTITOUCH_END;
460
0
  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr, nullptr));
461
0
  EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
462
0
}
463
464
0
TEST_F(APZHitTestingTester, TestRepaintFlushOnWheelEvents) {
465
0
  // The purpose of this test is to ensure that wheel events trigger a repaint
466
0
  // flush as per bug 1166871, and that the wheel event untransform is a no-op.
467
0
468
0
  CreateSimpleScrollingLayer();
469
0
  ScopedLayerTreeRegistration registration(manager, LayersId{0}, root, mcc);
470
0
  manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0);
471
0
  TestAsyncPanZoomController* apzcroot = ApzcOf(root);
472
0
473
0
  EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(3));
474
0
  ScreenPoint origin(100, 50);
475
0
  for (int i = 0; i < 3; i++) {
476
0
    ScrollWheelInput swi(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0,
477
0
      ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
478
0
      origin, 0, 10, false, WheelDeltaAdjustmentStrategy::eNone);
479
0
    EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(swi, nullptr, nullptr));
480
0
    EXPECT_EQ(origin, swi.mOrigin);
481
0
482
0
    AsyncTransform viewTransform;
483
0
    ParentLayerPoint point;
484
0
    apzcroot->SampleContentTransformForFrame(&viewTransform, point);
485
0
    EXPECT_EQ(0, point.x);
486
0
    EXPECT_EQ((i + 1) * 10, point.y);
487
0
    EXPECT_EQ(0, viewTransform.mTranslation.x);
488
0
    EXPECT_EQ((i + 1) * -10, viewTransform.mTranslation.y);
489
0
490
0
    mcc->AdvanceByMillis(5);
491
0
  }
492
0
}
493
494
0
TEST_F(APZHitTestingTester, TestForceDisableApz) {
495
0
  CreateSimpleScrollingLayer();
496
0
  DisableApzOn(root);
497
0
  ScopedLayerTreeRegistration registration(manager, LayersId{0}, root, mcc);
498
0
  manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0);
499
0
  TestAsyncPanZoomController* apzcroot = ApzcOf(root);
500
0
501
0
  ScreenPoint origin(100, 50);
502
0
  ScrollWheelInput swi(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0,
503
0
    ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
504
0
    origin, 0, 10, false, WheelDeltaAdjustmentStrategy::eNone);
505
0
  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(swi, nullptr, nullptr));
506
0
  EXPECT_EQ(origin, swi.mOrigin);
507
0
508
0
  AsyncTransform viewTransform;
509
0
  ParentLayerPoint point;
510
0
  apzcroot->SampleContentTransformForFrame(&viewTransform, point);
511
0
  // Since APZ is force-disabled, we expect to see the async transform via
512
0
  // the NORMAL AsyncMode, but not via the RESPECT_FORCE_DISABLE AsyncMode.
513
0
  EXPECT_EQ(0, point.x);
514
0
  EXPECT_EQ(10, point.y);
515
0
  EXPECT_EQ(0, viewTransform.mTranslation.x);
516
0
  EXPECT_EQ(-10, viewTransform.mTranslation.y);
517
0
  viewTransform = apzcroot->GetCurrentAsyncTransform(AsyncPanZoomController::eForCompositing);
518
0
  point = apzcroot->GetCurrentAsyncScrollOffset(AsyncPanZoomController::eForCompositing);
519
0
  EXPECT_EQ(0, point.x);
520
0
  EXPECT_EQ(0, point.y);
521
0
  EXPECT_EQ(0, viewTransform.mTranslation.x);
522
0
  EXPECT_EQ(0, viewTransform.mTranslation.y);
523
0
524
0
  mcc->AdvanceByMillis(10);
525
0
526
0
  // With untransforming events we should get normal behaviour (in this case,
527
0
  // no noticeable untransform, because the repaint request already got
528
0
  // flushed).
529
0
  swi = ScrollWheelInput(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0,
530
0
    ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
531
0
    origin, 0, 0, false, WheelDeltaAdjustmentStrategy::eNone);
532
0
  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(swi, nullptr, nullptr));
533
0
  EXPECT_EQ(origin, swi.mOrigin);
534
0
}
535
536
0
TEST_F(APZHitTestingTester, Bug1148350) {
537
0
  CreateBug1148350LayerTree();
538
0
  ScopedLayerTreeRegistration registration(manager, LayersId{0}, root, mcc);
539
0
  manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0);
540
0
541
0
  MockFunction<void(std::string checkPointName)> check;
542
0
  {
543
0
    InSequence s;
544
0
    EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(100, 100), 0, ApzcOf(layers[1])->GetGuid(), _)).Times(1);
545
0
    EXPECT_CALL(check, Call("Tapped without transform"));
546
0
    EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(100, 100), 0, ApzcOf(layers[1])->GetGuid(), _)).Times(1);
547
0
    EXPECT_CALL(check, Call("Tapped with interleaved transform"));
548
0
  }
549
0
550
0
  Tap(manager, ScreenIntPoint(100, 100), TimeDuration::FromMilliseconds(100));
551
0
  mcc->RunThroughDelayedTasks();
552
0
  check.Call("Tapped without transform");
553
0
554
0
  uint64_t blockId;
555
0
  TouchDown(manager, ScreenIntPoint(100, 100), mcc->Time(), &blockId);
556
0
  if (gfxPrefs::TouchActionEnabled()) {
557
0
    SetDefaultAllowedTouchBehavior(manager, blockId);
558
0
  }
559
0
  mcc->AdvanceByMillis(100);
560
0
561
0
  layers[0]->SetVisibleRegion(LayerIntRegion(LayerIntRect(0,50,200,150)));
562
0
  layers[0]->SetBaseTransform(Matrix4x4::Translation(0, 50, 0));
563
0
  manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0);
564
0
565
0
  TouchUp(manager, ScreenIntPoint(100, 100), mcc->Time());
566
0
  mcc->RunThroughDelayedTasks();
567
0
  check.Call("Tapped with interleaved transform");
568
0
}
569
570
0
TEST_F(APZHitTestingTester, HitTestingRespectsScrollClip_Bug1257288) {
571
0
  // Create the layer tree.
572
0
  const char* layerTreeSyntax = "c(tt)";
573
0
  // LayerID                     0 12
574
0
  nsIntRegion layerVisibleRegion[] = {
575
0
    nsIntRegion(IntRect(0,0,200,200)),
576
0
    nsIntRegion(IntRect(0,0,200,200)),
577
0
    nsIntRegion(IntRect(0,0,200,100))
578
0
  };
579
0
  root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
580
0
581
0
  // Add root scroll metadata to the first painted layer.
582
0
  SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID, CSSRect(0,0,200,200));
583
0
584
0
  // Add root and subframe scroll metadata to the second painted layer.
585
0
  // Give the subframe metadata a scroll clip corresponding to the subframe's
586
0
  // composition bounds.
587
0
  // Importantly, give the layer a layer clip which leaks outside of the
588
0
  // subframe's composition bounds.
589
0
  ScrollMetadata rootMetadata = BuildScrollMetadata(
590
0
      FrameMetrics::START_SCROLL_ID, CSSRect(0,0,200,200),
591
0
      ParentLayerRect(0,0,200,200));
592
0
  ScrollMetadata subframeMetadata = BuildScrollMetadata(
593
0
      FrameMetrics::START_SCROLL_ID + 1, CSSRect(0,0,200,200),
594
0
      ParentLayerRect(0,0,200,100));
595
0
  subframeMetadata.SetScrollClip(Some(LayerClip(ParentLayerIntRect(0,0,200,100))));
596
0
  layers[2]->SetScrollMetadata({subframeMetadata, rootMetadata});
597
0
  layers[2]->SetClipRect(Some(ParentLayerIntRect(0,0,200,200)));
598
0
  SetEventRegionsBasedOnBottommostMetrics(layers[2]);
599
0
600
0
  // Build the hit testing tree.
601
0
  ScopedLayerTreeRegistration registration(manager, LayersId{0}, root, mcc);
602
0
  manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0);
603
0
604
0
  // Pan on a region that's inside layers[2]'s layer clip, but outside
605
0
  // its subframe metadata's scroll clip.
606
0
  Pan(manager, 120, 110);
607
0
608
0
  // Test that the subframe hasn't scrolled.
609
0
  EXPECT_EQ(CSSPoint(0,0), ApzcOf(layers[2], 0)->GetFrameMetrics().GetScrollOffset());
610
0
}