|
1 /* vim:set ts=2 sw=2 sts=2 et: */ |
|
2 /* Any copyright is dedicated to the Public Domain. |
|
3 * http://creativecommons.org/publicdomain/zero/1.0/ |
|
4 */ |
|
5 |
|
6 #include "TestLayers.h" |
|
7 #include "gtest/gtest.h" |
|
8 #include "gmock/gmock.h" |
|
9 |
|
10 using namespace mozilla; |
|
11 using namespace mozilla::gfx; |
|
12 using namespace mozilla::layers; |
|
13 |
|
14 class TestLayerManager: public LayerManager { |
|
15 public: |
|
16 TestLayerManager() |
|
17 : LayerManager() |
|
18 {} |
|
19 |
|
20 virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) { return false; } |
|
21 virtual already_AddRefed<ContainerLayer> CreateContainerLayer() { return nullptr; } |
|
22 virtual void GetBackendName(nsAString& aName) {} |
|
23 virtual LayersBackend GetBackendType() { return LayersBackend::LAYERS_BASIC; } |
|
24 virtual void BeginTransaction() {} |
|
25 virtual already_AddRefed<ImageLayer> CreateImageLayer() { return nullptr; } |
|
26 virtual void SetRoot(Layer* aLayer) {} |
|
27 virtual already_AddRefed<ColorLayer> CreateColorLayer() { return nullptr; } |
|
28 virtual void BeginTransactionWithTarget(gfxContext* aTarget) {} |
|
29 virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() { return nullptr; } |
|
30 virtual void EndTransaction(DrawThebesLayerCallback aCallback, |
|
31 void* aCallbackData, |
|
32 EndTransactionFlags aFlags = END_DEFAULT) {} |
|
33 virtual int32_t GetMaxTextureSize() const { return 0; } |
|
34 virtual already_AddRefed<ThebesLayer> CreateThebesLayer() { return nullptr; } |
|
35 }; |
|
36 |
|
37 class TestContainerLayer: public ContainerLayer { |
|
38 public: |
|
39 TestContainerLayer(LayerManager* aManager) |
|
40 : ContainerLayer(aManager, nullptr) |
|
41 {} |
|
42 |
|
43 virtual const char* Name() const { |
|
44 return "TestContainerLayer"; |
|
45 } |
|
46 |
|
47 virtual LayerType GetType() const { |
|
48 return TYPE_CONTAINER; |
|
49 } |
|
50 |
|
51 virtual void ComputeEffectiveTransforms(const Matrix4x4& aTransformToSurface) { |
|
52 DefaultComputeEffectiveTransforms(aTransformToSurface); |
|
53 } |
|
54 }; |
|
55 |
|
56 class TestThebesLayer: public ThebesLayer { |
|
57 public: |
|
58 TestThebesLayer(LayerManager* aManager) |
|
59 : ThebesLayer(aManager, nullptr) |
|
60 {} |
|
61 |
|
62 virtual const char* Name() const { |
|
63 return "TestThebesLayer"; |
|
64 } |
|
65 |
|
66 virtual LayerType GetType() const { |
|
67 return TYPE_THEBES; |
|
68 } |
|
69 |
|
70 virtual void InvalidateRegion(const nsIntRegion& aRegion) { |
|
71 MOZ_CRASH(); |
|
72 } |
|
73 }; |
|
74 |
|
75 class TestUserData: public LayerUserData { |
|
76 public: |
|
77 MOCK_METHOD0(Die, void()); |
|
78 virtual ~TestUserData() { Die(); } |
|
79 }; |
|
80 |
|
81 |
|
82 TEST(Layers, LayerConstructor) { |
|
83 TestContainerLayer layer(nullptr); |
|
84 } |
|
85 |
|
86 TEST(Layers, Defaults) { |
|
87 TestContainerLayer layer(nullptr); |
|
88 ASSERT_EQ(1.0, layer.GetOpacity()); |
|
89 ASSERT_EQ(1.0f, layer.GetPostXScale()); |
|
90 ASSERT_EQ(1.0f, layer.GetPostYScale()); |
|
91 |
|
92 ASSERT_EQ(nullptr, layer.GetNextSibling()); |
|
93 ASSERT_EQ(nullptr, layer.GetPrevSibling()); |
|
94 ASSERT_EQ(nullptr, layer.GetFirstChild()); |
|
95 ASSERT_EQ(nullptr, layer.GetLastChild()); |
|
96 } |
|
97 |
|
98 TEST(Layers, Transform) { |
|
99 TestContainerLayer layer(nullptr); |
|
100 |
|
101 Matrix4x4 identity; |
|
102 ASSERT_EQ(true, identity.IsIdentity()); |
|
103 |
|
104 ASSERT_EQ(identity, layer.GetTransform()); |
|
105 } |
|
106 |
|
107 TEST(Layers, Type) { |
|
108 TestContainerLayer layer(nullptr); |
|
109 ASSERT_EQ(nullptr, layer.AsThebesLayer()); |
|
110 ASSERT_EQ(nullptr, layer.AsRefLayer()); |
|
111 ASSERT_EQ(nullptr, layer.AsColorLayer()); |
|
112 } |
|
113 |
|
114 TEST(Layers, UserData) { |
|
115 TestContainerLayer* layerPtr = new TestContainerLayer(nullptr); |
|
116 TestContainerLayer& layer = *layerPtr; |
|
117 |
|
118 void* key1 = (void*)1; |
|
119 void* key2 = (void*)2; |
|
120 void* key3 = (void*)3; |
|
121 |
|
122 TestUserData* data1 = new TestUserData; |
|
123 TestUserData* data2 = new TestUserData; |
|
124 TestUserData* data3 = new TestUserData; |
|
125 |
|
126 ASSERT_EQ(nullptr, layer.GetUserData(key1)); |
|
127 ASSERT_EQ(nullptr, layer.GetUserData(key2)); |
|
128 ASSERT_EQ(nullptr, layer.GetUserData(key3)); |
|
129 |
|
130 layer.SetUserData(key1, data1); |
|
131 layer.SetUserData(key2, data2); |
|
132 layer.SetUserData(key3, data3); |
|
133 |
|
134 // Also checking that the user data is returned but not free'd |
|
135 ASSERT_EQ(data1, layer.RemoveUserData(key1).forget()); |
|
136 ASSERT_EQ(data2, layer.RemoveUserData(key2).forget()); |
|
137 ASSERT_EQ(data3, layer.RemoveUserData(key3).forget()); |
|
138 |
|
139 layer.SetUserData(key1, data1); |
|
140 layer.SetUserData(key2, data2); |
|
141 layer.SetUserData(key3, data3); |
|
142 |
|
143 // Layer has ownership of data1-3, check that they are destroyed |
|
144 EXPECT_CALL(*data1, Die()); |
|
145 EXPECT_CALL(*data2, Die()); |
|
146 EXPECT_CALL(*data3, Die()); |
|
147 delete layerPtr; |
|
148 |
|
149 } |
|
150 |
|
151 static |
|
152 already_AddRefed<Layer> CreateLayer(char aLayerType, LayerManager* aManager) { |
|
153 nsRefPtr<Layer> layer = nullptr; |
|
154 if (aLayerType == 'c') { |
|
155 layer = new TestContainerLayer(aManager); |
|
156 } else if (aLayerType == 't') { |
|
157 layer = new TestThebesLayer(aManager); |
|
158 } |
|
159 return layer.forget(); |
|
160 } |
|
161 |
|
162 already_AddRefed<Layer> CreateLayerTree( |
|
163 const char* aLayerTreeDescription, |
|
164 nsIntRegion* aVisibleRegions, |
|
165 const gfx3DMatrix* aTransforms, |
|
166 nsRefPtr<LayerManager>& manager, |
|
167 nsTArray<nsRefPtr<Layer> >& aLayersOut) { |
|
168 |
|
169 aLayersOut.Clear(); |
|
170 |
|
171 manager = new TestLayerManager(); |
|
172 |
|
173 nsRefPtr<Layer> rootLayer = nullptr; |
|
174 nsRefPtr<ContainerLayer> parentContainerLayer = nullptr; |
|
175 nsRefPtr<Layer> lastLayer = nullptr; |
|
176 int layerNumber = 0; |
|
177 for (size_t i = 0; i < strlen(aLayerTreeDescription); i++) { |
|
178 if (aLayerTreeDescription[i] == '(') { |
|
179 if (!lastLayer) { |
|
180 printf("Syntax error, likely '(' character isn't preceded by a container.\n"); |
|
181 MOZ_CRASH(); |
|
182 } |
|
183 parentContainerLayer = lastLayer->AsContainerLayer(); |
|
184 if (!parentContainerLayer) { |
|
185 printf("Layer before '(' must be a container.\n"); |
|
186 MOZ_CRASH(); |
|
187 } |
|
188 } else if (aLayerTreeDescription[i] == ')') { |
|
189 parentContainerLayer = parentContainerLayer->GetParent(); |
|
190 lastLayer = nullptr; |
|
191 } else { |
|
192 nsRefPtr<Layer> layer = CreateLayer(aLayerTreeDescription[i], manager.get()); |
|
193 if (aVisibleRegions) { |
|
194 layer->SetVisibleRegion(aVisibleRegions[layerNumber]); |
|
195 } |
|
196 if (aTransforms) { |
|
197 Matrix4x4 transform; |
|
198 ToMatrix4x4(aTransforms[layerNumber], transform); |
|
199 layer->SetBaseTransform(transform); |
|
200 } |
|
201 aLayersOut.AppendElement(layer); |
|
202 layerNumber++; |
|
203 if (rootLayer && !parentContainerLayer) { |
|
204 MOZ_CRASH(); |
|
205 } |
|
206 if (!rootLayer) { |
|
207 rootLayer = layer; |
|
208 } |
|
209 if (parentContainerLayer) { |
|
210 parentContainerLayer->InsertAfter(layer, parentContainerLayer->GetLastChild()); |
|
211 layer->SetParent(parentContainerLayer); |
|
212 } |
|
213 lastLayer = layer; |
|
214 } |
|
215 } |
|
216 if (rootLayer) { |
|
217 rootLayer->ComputeEffectiveTransforms(Matrix4x4()); |
|
218 } |
|
219 return rootLayer.forget(); |
|
220 } |
|
221 |
|
222 TEST(Layers, LayerTree) { |
|
223 const char* layerTreeSyntax = "c(c(tt))"; |
|
224 nsIntRegion layerVisibleRegion[] = { |
|
225 nsIntRegion(nsIntRect(0,0,100,100)), |
|
226 nsIntRegion(nsIntRect(0,0,100,100)), |
|
227 nsIntRegion(nsIntRect(0,0,100,100)), |
|
228 nsIntRegion(nsIntRect(10,10,20,20)), |
|
229 }; |
|
230 gfx3DMatrix transforms[] = { |
|
231 gfx3DMatrix(), |
|
232 gfx3DMatrix(), |
|
233 gfx3DMatrix(), |
|
234 gfx3DMatrix(), |
|
235 }; |
|
236 nsTArray<nsRefPtr<Layer> > layers; |
|
237 |
|
238 nsRefPtr<LayerManager> lm; |
|
239 nsRefPtr<Layer> root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, lm, layers); |
|
240 |
|
241 // B2G g++ doesn't like ASSERT_NE with nullptr directly. It thinks it's |
|
242 // an int. |
|
243 Layer* nullLayer = nullptr; |
|
244 ASSERT_NE(nullLayer, layers[0]->AsContainerLayer()); |
|
245 ASSERT_NE(nullLayer, layers[1]->AsContainerLayer()); |
|
246 ASSERT_NE(nullLayer, layers[2]->AsThebesLayer()); |
|
247 ASSERT_NE(nullLayer, layers[3]->AsThebesLayer()); |
|
248 } |
|
249 |
|
250 static void ValidateTreePointers(Layer* aLayer) { |
|
251 if (aLayer->GetNextSibling()) { |
|
252 ASSERT_EQ(aLayer, aLayer->GetNextSibling()->GetPrevSibling()); |
|
253 } else if (aLayer->GetParent()) { |
|
254 ASSERT_EQ(aLayer, aLayer->GetParent()->GetLastChild()); |
|
255 } |
|
256 if (aLayer->GetPrevSibling()) { |
|
257 ASSERT_EQ(aLayer, aLayer->GetPrevSibling()->GetNextSibling()); |
|
258 } else if (aLayer->GetParent()) { |
|
259 ASSERT_EQ(aLayer, aLayer->GetParent()->GetFirstChild()); |
|
260 } |
|
261 if (aLayer->GetFirstChild()) { |
|
262 ASSERT_EQ(aLayer, aLayer->GetFirstChild()->GetParent()); |
|
263 } |
|
264 if (aLayer->GetLastChild()) { |
|
265 ASSERT_EQ(aLayer, aLayer->GetLastChild()->GetParent()); |
|
266 } |
|
267 } |
|
268 |
|
269 static void ValidateTreePointers(nsTArray<nsRefPtr<Layer> >& aLayers) { |
|
270 for (uint32_t i = 0; i < aLayers.Length(); i++) { |
|
271 ValidateTreePointers(aLayers[i]); |
|
272 } |
|
273 } |
|
274 |
|
275 TEST(Layers, RepositionChild) { |
|
276 const char* layerTreeSyntax = "c(ttt)"; |
|
277 |
|
278 nsTArray<nsRefPtr<Layer> > layers; |
|
279 nsRefPtr<LayerManager> lm; |
|
280 nsRefPtr<Layer> root = CreateLayerTree(layerTreeSyntax, nullptr, nullptr, lm, layers); |
|
281 ContainerLayer* parent = root->AsContainerLayer(); |
|
282 ValidateTreePointers(layers); |
|
283 |
|
284 // tree is currently like this (using indexes into layers): |
|
285 // 0 |
|
286 // 1 2 3 |
|
287 ASSERT_EQ(layers[2], layers[1]->GetNextSibling()); |
|
288 ASSERT_EQ(layers[3], layers[2]->GetNextSibling()); |
|
289 ASSERT_EQ(nullptr, layers[3]->GetNextSibling()); |
|
290 |
|
291 parent->RepositionChild(layers[1], layers[3]); |
|
292 ValidateTreePointers(layers); |
|
293 |
|
294 // now the tree is like this: |
|
295 // 0 |
|
296 // 2 3 1 |
|
297 ASSERT_EQ(layers[3], layers[2]->GetNextSibling()); |
|
298 ASSERT_EQ(layers[1], layers[3]->GetNextSibling()); |
|
299 ASSERT_EQ(nullptr, layers[1]->GetNextSibling()); |
|
300 |
|
301 parent->RepositionChild(layers[3], layers[2]); |
|
302 ValidateTreePointers(layers); |
|
303 |
|
304 // no change |
|
305 ASSERT_EQ(layers[3], layers[2]->GetNextSibling()); |
|
306 ASSERT_EQ(layers[1], layers[3]->GetNextSibling()); |
|
307 ASSERT_EQ(nullptr, layers[1]->GetNextSibling()); |
|
308 |
|
309 parent->RepositionChild(layers[3], layers[1]); |
|
310 ValidateTreePointers(layers); |
|
311 |
|
312 // 0 |
|
313 // 2 1 3 |
|
314 ASSERT_EQ(layers[1], layers[2]->GetNextSibling()); |
|
315 ASSERT_EQ(layers[3], layers[1]->GetNextSibling()); |
|
316 ASSERT_EQ(nullptr, layers[3]->GetNextSibling()); |
|
317 |
|
318 parent->RepositionChild(layers[3], nullptr); |
|
319 ValidateTreePointers(layers); |
|
320 |
|
321 // 0 |
|
322 // 3 2 1 |
|
323 ASSERT_EQ(layers[2], layers[3]->GetNextSibling()); |
|
324 ASSERT_EQ(layers[1], layers[2]->GetNextSibling()); |
|
325 ASSERT_EQ(nullptr, layers[1]->GetNextSibling()); |
|
326 } |