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