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 "TestWRScrollData.h"
8 #include "APZTestAccess.h"
9 #include "gtest/gtest.h"
10 #include "FrameMetrics.h"
11 #include "gfxPlatform.h"
12 #include "mozilla/layers/APZUpdater.h"
13 #include "mozilla/layers/LayersTypes.h"
14 #include "mozilla/layers/ScrollableLayerGuid.h"
15 #include "mozilla/layers/WebRenderScrollDataWrapper.h"
16 #include "mozilla/UniquePtr.h"
17 #include "apz/src/APZCTreeManager.h"
18 
19 using mozilla::layers::APZCTreeManager;
20 using mozilla::layers::APZUpdater;
21 using mozilla::layers::LayersId;
22 using mozilla::layers::ScrollableLayerGuid;
23 using mozilla::layers::ScrollMetadata;
24 using mozilla::layers::TestWRScrollData;
25 using mozilla::layers::WebRenderLayerScrollData;
26 using mozilla::layers::WebRenderScrollDataWrapper;
27 
28 /* static */
Create(const char * aTreeShape,const APZUpdater & aUpdater,const nsIntRegion * aVisibleRegions,const gfx::Matrix4x4 * aTransforms)29 TestWRScrollData TestWRScrollData::Create(const char* aTreeShape,
30                                           const APZUpdater& aUpdater,
31                                           const nsIntRegion* aVisibleRegions,
32                                           const gfx::Matrix4x4* aTransforms) {
33   // The WebRenderLayerScrollData tree needs to be created in a fairly
34   // particular way (for example, each node needs to know the number of
35   // descendants it has), so this function takes care to create the nodes
36   // in the same order as WebRenderCommandBuilder would.
37   TestWRScrollData result;
38   const size_t len = strlen(aTreeShape);
39   // "Layer index" in this function refers to the index by which a layer will
40   // be accessible via TestWRScrollData::GetLayer(), and matches the order
41   // in which the layer appears in |aTreeShape|.
42   size_t currentLayerIndex = 0;
43   struct LayerEntry {
44     size_t mLayerIndex;
45     int32_t mDescendantCount = 0;
46   };
47   // Layers we have encountered in |aTreeShape|, but have not built a
48   // WebRenderLayerScrollData for. (It can only be built after its
49   // descendants have been encountered and counted.)
50   std::stack<LayerEntry> pendingLayers;
51   std::vector<WebRenderLayerScrollData> finishedLayers;
52   // Tracks the level of nesting of '(' characters. Starts at 1 to account
53   // for the root layer.
54   size_t depth = 1;
55   // Helper function for finishing a layer once all its descendants have been
56   // encountered.
57   auto finishLayer = [&] {
58     MOZ_ASSERT(!pendingLayers.empty());
59     LayerEntry entry = pendingLayers.top();
60 
61     WebRenderLayerScrollData layer;
62     APZTestAccess::InitializeForTest(layer, entry.mDescendantCount);
63     if (aVisibleRegions) {
64       layer.SetVisibleRegion(LayerIntRegion::FromUnknownRegion(
65           aVisibleRegions[entry.mLayerIndex]));
66     }
67     if (aTransforms) {
68       layer.SetTransform(aTransforms[entry.mLayerIndex]);
69     }
70     finishedLayers.push_back(std::move(layer));
71 
72     // |finishedLayers| stores the layers in a different order than they
73     // appeared in |aTreeShape|. To be able to access layers by their layer
74     // index, keep a mapping from layer index to index in |finishedLayers|.
75     result.mIndexMap.emplace(entry.mLayerIndex, finishedLayers.size() - 1);
76 
77     pendingLayers.pop();
78 
79     // Keep track of descendant counts. The +1 is for the layer just finished.
80     if (!pendingLayers.empty()) {
81       pendingLayers.top().mDescendantCount += (entry.mDescendantCount + 1);
82     }
83   };
84   for (size_t i = 0; i < len; ++i) {
85     if (aTreeShape[i] == '(') {
86       ++depth;
87     } else if (aTreeShape[i] == ')') {
88       if (pendingLayers.size() <= 1) {
89         printf("Invalid tree shape: too many ')'\n");
90         MOZ_CRASH();
91       }
92       finishLayer();  // finish last layer at current depth
93       --depth;
94     } else {
95       if (aTreeShape[i] != 'x') {
96         printf("The only allowed character to represent a layer is 'x'\n");
97         MOZ_CRASH();
98       }
99       if (depth == pendingLayers.size()) {
100         // We have a previous layer at this same depth to finish.
101         if (depth <= 1) {
102           printf("The tree is only allowed to have one root\n");
103           MOZ_CRASH();
104         }
105         finishLayer();
106       }
107       MOZ_ASSERT(depth == pendingLayers.size() + 1);
108       pendingLayers.push({currentLayerIndex});
109       ++currentLayerIndex;
110     }
111   }
112   if (pendingLayers.size() != 1) {
113     printf("Invalid tree shape: '(' and ')' not balanced\n");
114     MOZ_CRASH();
115   }
116   finishLayer();  // finish root layer
117 
118   // As in WebRenderCommandBuilder, the layers need to be added to the
119   // WebRenderScrollData in reverse of the order in which they were built.
120   for (auto it = finishedLayers.rbegin(); it != finishedLayers.rend(); ++it) {
121     result.AddLayerData(std::move(*it));
122   }
123   // mIndexMap also needs to be adjusted to accout for the reversal above.
124   for (auto& [layerIndex, storedIndex] : result.mIndexMap) {
125     (void)layerIndex;  // suppress -Werror=unused-variable
126     storedIndex = result.GetLayerCount() - storedIndex - 1;
127   }
128 
129   return result;
130 }
131 
operator [](size_t aLayerIndex) const132 const WebRenderLayerScrollData* TestWRScrollData::operator[](
133     size_t aLayerIndex) const {
134   auto it = mIndexMap.find(aLayerIndex);
135   if (it == mIndexMap.end()) {
136     return nullptr;
137   }
138   return GetLayerData(it->second);
139 }
140 
operator [](size_t aLayerIndex)141 WebRenderLayerScrollData* TestWRScrollData::operator[](size_t aLayerIndex) {
142   auto it = mIndexMap.find(aLayerIndex);
143   if (it == mIndexMap.end()) {
144     return nullptr;
145   }
146   return GetLayerData(it->second);
147 }
148 
SetScrollMetadata(size_t aLayerIndex,const nsTArray<ScrollMetadata> & aMetadata)149 void TestWRScrollData::SetScrollMetadata(
150     size_t aLayerIndex, const nsTArray<ScrollMetadata>& aMetadata) {
151   WebRenderLayerScrollData* layer = operator[](aLayerIndex);
152   MOZ_ASSERT(layer);
153   for (const ScrollMetadata& metadata : aMetadata) {
154     layer->AppendScrollMetadata(*this, metadata);
155   }
156 }
157 
158 class WebRenderScrollDataWrapperTester : public ::testing::Test {
159  protected:
SetUp()160   virtual void SetUp() {
161     // This ensures ScrollMetadata::sNullMetadata is initialized.
162     gfxPlatform::GetPlatform();
163 
164     mManager = new APZCTreeManager(LayersId{0});
165     mUpdater = new APZUpdater(mManager, false);
166   }
167 
168   RefPtr<APZCTreeManager> mManager;
169   RefPtr<APZUpdater> mUpdater;
170 };
171 
TEST_F(WebRenderScrollDataWrapperTester,SimpleTree)172 TEST_F(WebRenderScrollDataWrapperTester, SimpleTree) {
173   auto layers = TestWRScrollData::Create("x(x(x(xx)x(x)))", *mUpdater);
174   WebRenderScrollDataWrapper w0(*mUpdater, &layers);
175 
176   ASSERT_EQ(layers[0], w0.GetLayer());
177   WebRenderScrollDataWrapper w1 = w0.GetLastChild();
178   ASSERT_EQ(layers[1], w1.GetLayer());
179   ASSERT_FALSE(w1.GetPrevSibling().IsValid());
180   WebRenderScrollDataWrapper w5 = w1.GetLastChild();
181   ASSERT_EQ(layers[5], w5.GetLayer());
182   WebRenderScrollDataWrapper w6 = w5.GetLastChild();
183   ASSERT_EQ(layers[6], w6.GetLayer());
184   ASSERT_FALSE(w6.GetLastChild().IsValid());
185   WebRenderScrollDataWrapper w2 = w5.GetPrevSibling();
186   ASSERT_EQ(layers[2], w2.GetLayer());
187   ASSERT_FALSE(w2.GetPrevSibling().IsValid());
188   WebRenderScrollDataWrapper w4 = w2.GetLastChild();
189   ASSERT_EQ(layers[4], w4.GetLayer());
190   ASSERT_FALSE(w4.GetLastChild().IsValid());
191   WebRenderScrollDataWrapper w3 = w4.GetPrevSibling();
192   ASSERT_EQ(layers[3], w3.GetLayer());
193   ASSERT_FALSE(w3.GetLastChild().IsValid());
194   ASSERT_FALSE(w3.GetPrevSibling().IsValid());
195 }
196 
MakeMetadata(ScrollableLayerGuid::ViewID aId)197 static ScrollMetadata MakeMetadata(ScrollableLayerGuid::ViewID aId) {
198   ScrollMetadata metadata;
199   metadata.GetMetrics().SetScrollId(aId);
200   return metadata;
201 }
202 
TEST_F(WebRenderScrollDataWrapperTester,MultiFramemetricsTree)203 TEST_F(WebRenderScrollDataWrapperTester, MultiFramemetricsTree) {
204   auto layers = TestWRScrollData::Create("x(x(x(xx)x(x)))", *mUpdater);
205 
206   nsTArray<ScrollMetadata> metadata;
207   metadata.InsertElementAt(0,
208                            MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID +
209                                         0));  // topmost of root layer
210   metadata.InsertElementAt(0,
211                            MakeMetadata(ScrollableLayerGuid::NULL_SCROLL_ID));
212   metadata.InsertElementAt(
213       0, MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID + 1));
214   metadata.InsertElementAt(
215       0, MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID + 2));
216   metadata.InsertElementAt(0,
217                            MakeMetadata(ScrollableLayerGuid::NULL_SCROLL_ID));
218   metadata.InsertElementAt(
219       0, MakeMetadata(
220              ScrollableLayerGuid::NULL_SCROLL_ID));  // bottom of root layer
221   layers.SetScrollMetadata(0, metadata);
222 
223   metadata.Clear();
224   metadata.InsertElementAt(
225       0, MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID + 3));
226   layers.SetScrollMetadata(1, metadata);
227 
228   metadata.Clear();
229   metadata.InsertElementAt(
230       0, MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID + 4));
231   layers.SetScrollMetadata(2, metadata);
232 
233   metadata.Clear();
234   metadata.InsertElementAt(
235       0, MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID + 5));
236   layers.SetScrollMetadata(4, metadata);
237 
238   metadata.Clear();
239   metadata.InsertElementAt(0,
240                            MakeMetadata(ScrollableLayerGuid::NULL_SCROLL_ID));
241   metadata.InsertElementAt(
242       0, MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID + 6));
243   layers.SetScrollMetadata(5, metadata);
244 
245   WebRenderScrollDataWrapper wrapper(*mUpdater, &layers);
246   nsTArray<WebRenderLayerScrollData*> expectedLayers;
247   expectedLayers.AppendElement(layers[0]);
248   expectedLayers.AppendElement(layers[0]);
249   expectedLayers.AppendElement(layers[0]);
250   expectedLayers.AppendElement(layers[0]);
251   expectedLayers.AppendElement(layers[0]);
252   expectedLayers.AppendElement(layers[0]);
253   expectedLayers.AppendElement(layers[1]);
254   expectedLayers.AppendElement(layers[5]);
255   expectedLayers.AppendElement(layers[5]);
256   expectedLayers.AppendElement(layers[6]);
257   nsTArray<ScrollableLayerGuid::ViewID> expectedIds;
258   expectedIds.AppendElement(ScrollableLayerGuid::START_SCROLL_ID + 0);
259   expectedIds.AppendElement(ScrollableLayerGuid::NULL_SCROLL_ID);
260   expectedIds.AppendElement(ScrollableLayerGuid::START_SCROLL_ID + 1);
261   expectedIds.AppendElement(ScrollableLayerGuid::START_SCROLL_ID + 2);
262   expectedIds.AppendElement(ScrollableLayerGuid::NULL_SCROLL_ID);
263   expectedIds.AppendElement(ScrollableLayerGuid::NULL_SCROLL_ID);
264   expectedIds.AppendElement(ScrollableLayerGuid::START_SCROLL_ID + 3);
265   expectedIds.AppendElement(ScrollableLayerGuid::NULL_SCROLL_ID);
266   expectedIds.AppendElement(ScrollableLayerGuid::START_SCROLL_ID + 6);
267   expectedIds.AppendElement(ScrollableLayerGuid::NULL_SCROLL_ID);
268   for (int i = 0; i < 10; i++) {
269     ASSERT_EQ(expectedLayers[i], wrapper.GetLayer());
270     ASSERT_EQ(expectedIds[i], wrapper.Metrics().GetScrollId());
271     wrapper = wrapper.GetLastChild();
272   }
273   ASSERT_FALSE(wrapper.IsValid());
274 }
275