1/*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "painting/PaintAggregator.h"
33
34#include <gtest/gtest.h>
35
36using namespace WebCore;
37using namespace WebKit;
38
39namespace {
40
41TEST(PaintAggregator, InitialState)
42{
43    PaintAggregator greg;
44    EXPECT_FALSE(greg.hasPendingUpdate());
45}
46
47TEST(PaintAggregator, SingleInvalidation)
48{
49    PaintAggregator greg;
50
51    IntRect rect(2, 4, 10, 16);
52    greg.invalidateRect(rect);
53
54    EXPECT_TRUE(greg.hasPendingUpdate());
55    PaintAggregator::PendingUpdate update;
56    greg.popPendingUpdate(&update);
57
58    EXPECT_TRUE(update.scrollRect.isEmpty());
59    ASSERT_EQ(1U, update.paintRects.size());
60
61    EXPECT_EQ(rect, update.paintRects[0]);
62}
63
64TEST(PaintAggregator, DoubleDisjointInvalidation)
65{
66    PaintAggregator greg;
67
68    IntRect r1(2, 4, 2, 40);
69    IntRect r2(4, 2, 40, 2);
70
71    greg.invalidateRect(r1);
72    greg.invalidateRect(r2);
73
74    IntRect expectedBounds = unionRect(r1, r2);
75
76    EXPECT_TRUE(greg.hasPendingUpdate());
77    PaintAggregator::PendingUpdate update;
78    greg.popPendingUpdate(&update);
79
80    EXPECT_TRUE(update.scrollRect.isEmpty());
81    EXPECT_EQ(2U, update.paintRects.size());
82
83    EXPECT_EQ(expectedBounds, update.calculatePaintBounds());
84}
85
86TEST(PaintAggregator, DisjointInvalidationsCombined)
87{
88    PaintAggregator greg;
89
90    // Make the rectangles such that they don't overlap but cover a very large
91    // percentage of the area of covered by their union. This is so we're not
92    // very sensitive to the combining heuristic in the paint aggregator.
93    IntRect r1(2, 4, 2, 1000);
94    IntRect r2(5, 2, 2, 1000);
95
96    greg.invalidateRect(r1);
97    greg.invalidateRect(r2);
98
99    IntRect expectedBounds = unionRect(r1, r2);
100
101    EXPECT_TRUE(greg.hasPendingUpdate());
102    PaintAggregator::PendingUpdate update;
103    greg.popPendingUpdate(&update);
104
105    EXPECT_TRUE(update.scrollRect.isEmpty());
106    ASSERT_EQ(1U, update.paintRects.size());
107
108    EXPECT_EQ(expectedBounds, update.paintRects[0]);
109}
110
111TEST(PaintAggregator, SingleScroll)
112{
113    PaintAggregator greg;
114
115    IntRect rect(1, 2, 3, 4);
116    IntPoint delta(1, 0);
117    greg.scrollRect(delta.x(), delta.y(), rect);
118
119    EXPECT_TRUE(greg.hasPendingUpdate());
120    PaintAggregator::PendingUpdate update;
121    greg.popPendingUpdate(&update);
122
123    EXPECT_TRUE(update.paintRects.isEmpty());
124    EXPECT_FALSE(update.scrollRect.isEmpty());
125
126    EXPECT_EQ(rect, update.scrollRect);
127
128    EXPECT_EQ(delta.x(), update.scrollDelta.x());
129    EXPECT_EQ(delta.y(), update.scrollDelta.y());
130
131    IntRect resultingDamage = update.calculateScrollDamage();
132    IntRect expectedDamage(1, 2, 1, 4);
133    EXPECT_EQ(expectedDamage, resultingDamage);
134}
135
136TEST(PaintAggregator, DoubleOverlappingScroll)
137{
138    PaintAggregator greg;
139
140    IntRect rect(1, 2, 3, 4);
141    IntPoint delta1(1, 0);
142    IntPoint delta2(1, 0);
143    greg.scrollRect(delta1.x(), delta1.y(), rect);
144    greg.scrollRect(delta2.x(), delta2.y(), rect);
145
146    EXPECT_TRUE(greg.hasPendingUpdate());
147    PaintAggregator::PendingUpdate update;
148    greg.popPendingUpdate(&update);
149
150    EXPECT_TRUE(update.paintRects.isEmpty());
151    EXPECT_FALSE(update.scrollRect.isEmpty());
152
153    EXPECT_EQ(rect, update.scrollRect);
154
155    IntPoint expectedDelta(delta1.x() + delta2.x(),
156                           delta1.y() + delta2.y());
157    EXPECT_EQ(expectedDelta.x(), update.scrollDelta.x());
158    EXPECT_EQ(expectedDelta.y(), update.scrollDelta.y());
159
160    IntRect resultingDamage = update.calculateScrollDamage();
161    IntRect expectedDamage(1, 2, 2, 4);
162    EXPECT_EQ(expectedDamage, resultingDamage);
163}
164
165TEST(PaintAggregator, NegatingScroll)
166{
167    PaintAggregator greg;
168
169    // Scroll twice in opposite directions by equal amounts. The result
170    // should be no scrolling.
171
172    IntRect rect(1, 2, 3, 4);
173    IntPoint delta1(1, 0);
174    IntPoint delta2(-1, 0);
175    greg.scrollRect(delta1.x(), delta1.y(), rect);
176    greg.scrollRect(delta2.x(), delta2.y(), rect);
177
178    EXPECT_FALSE(greg.hasPendingUpdate());
179}
180
181TEST(PaintAggregator, DiagonalScroll)
182{
183    PaintAggregator greg;
184
185    // We don't support optimized diagonal scrolling, so this should result in
186    // repainting.
187
188    IntRect rect(1, 2, 3, 4);
189    IntPoint delta(1, 1);
190    greg.scrollRect(delta.x(), delta.y(), rect);
191
192    EXPECT_TRUE(greg.hasPendingUpdate());
193    PaintAggregator::PendingUpdate update;
194    greg.popPendingUpdate(&update);
195
196    EXPECT_TRUE(update.scrollRect.isEmpty());
197    ASSERT_EQ(1U, update.paintRects.size());
198
199    EXPECT_EQ(rect, update.paintRects[0]);
200}
201
202TEST(PaintAggregator, ContainedPaintAfterScroll)
203{
204    PaintAggregator greg;
205
206    IntRect scrollRect(0, 0, 10, 10);
207    greg.scrollRect(2, 0, scrollRect);
208
209    IntRect paintRect(4, 4, 2, 2);
210    greg.invalidateRect(paintRect);
211
212    EXPECT_TRUE(greg.hasPendingUpdate());
213    PaintAggregator::PendingUpdate update;
214    greg.popPendingUpdate(&update);
215
216    // expecting a paint rect inside the scroll rect
217    EXPECT_FALSE(update.scrollRect.isEmpty());
218    EXPECT_EQ(1U, update.paintRects.size());
219
220    EXPECT_EQ(scrollRect, update.scrollRect);
221    EXPECT_EQ(paintRect, update.paintRects[0]);
222}
223
224TEST(PaintAggregator, ContainedPaintBeforeScroll)
225{
226    PaintAggregator greg;
227
228    IntRect paintRect(4, 4, 2, 2);
229    greg.invalidateRect(paintRect);
230
231    IntRect scrollRect(0, 0, 10, 10);
232    greg.scrollRect(2, 0, scrollRect);
233
234    EXPECT_TRUE(greg.hasPendingUpdate());
235    PaintAggregator::PendingUpdate update;
236    greg.popPendingUpdate(&update);
237
238    // Expecting a paint rect inside the scroll rect
239    EXPECT_FALSE(update.scrollRect.isEmpty());
240    EXPECT_EQ(1U, update.paintRects.size());
241
242    paintRect.move(2, 0);
243
244    EXPECT_EQ(scrollRect, update.scrollRect);
245    EXPECT_EQ(paintRect, update.paintRects[0]);
246}
247
248TEST(PaintAggregator, ContainedPaintsBeforeAndAfterScroll)
249{
250    PaintAggregator greg;
251
252    IntRect paintRect1(4, 4, 2, 2);
253    greg.invalidateRect(paintRect1);
254
255    IntRect scrollRect(0, 0, 10, 10);
256    greg.scrollRect(2, 0, scrollRect);
257
258    IntRect paintRect2(6, 4, 2, 2);
259    greg.invalidateRect(paintRect2);
260
261    IntRect expectedPaintRect = paintRect2;
262
263    EXPECT_TRUE(greg.hasPendingUpdate());
264    PaintAggregator::PendingUpdate update;
265    greg.popPendingUpdate(&update);
266
267    // Expecting a paint rect inside the scroll rect
268    EXPECT_FALSE(update.scrollRect.isEmpty());
269    EXPECT_EQ(1U, update.paintRects.size());
270
271    EXPECT_EQ(scrollRect, update.scrollRect);
272    EXPECT_EQ(expectedPaintRect, update.paintRects[0]);
273}
274
275TEST(PaintAggregator, LargeContainedPaintAfterScroll)
276{
277    PaintAggregator greg;
278
279    IntRect scrollRect(0, 0, 10, 10);
280    greg.scrollRect(0, 1, scrollRect);
281
282    IntRect paintRect(0, 0, 10, 9); // Repaint 90%
283    greg.invalidateRect(paintRect);
284
285    EXPECT_TRUE(greg.hasPendingUpdate());
286    PaintAggregator::PendingUpdate update;
287    greg.popPendingUpdate(&update);
288
289    EXPECT_TRUE(update.scrollRect.isEmpty());
290    EXPECT_EQ(1U, update.paintRects.size());
291
292    EXPECT_EQ(scrollRect, update.paintRects[0]);
293}
294
295TEST(PaintAggregator, LargeContainedPaintBeforeScroll)
296{
297    PaintAggregator greg;
298
299    IntRect paintRect(0, 0, 10, 9); // Repaint 90%
300    greg.invalidateRect(paintRect);
301
302    IntRect scrollRect(0, 0, 10, 10);
303    greg.scrollRect(0, 1, scrollRect);
304
305    EXPECT_TRUE(greg.hasPendingUpdate());
306    PaintAggregator::PendingUpdate update;
307    greg.popPendingUpdate(&update);
308
309    EXPECT_TRUE(update.scrollRect.isEmpty());
310    EXPECT_EQ(1U, update.paintRects.size());
311
312    EXPECT_EQ(scrollRect, update.paintRects[0]);
313}
314
315TEST(PaintAggregator, OverlappingPaintBeforeScroll)
316{
317    PaintAggregator greg;
318
319    IntRect paintRect(4, 4, 10, 2);
320    greg.invalidateRect(paintRect);
321
322    IntRect scrollRect(0, 0, 10, 10);
323    greg.scrollRect(2, 0, scrollRect);
324
325    IntRect expectedPaintRect = unionRect(scrollRect, paintRect);
326
327    EXPECT_TRUE(greg.hasPendingUpdate());
328    PaintAggregator::PendingUpdate update;
329    greg.popPendingUpdate(&update);
330
331    EXPECT_TRUE(update.scrollRect.isEmpty());
332    EXPECT_EQ(1U, update.paintRects.size());
333
334    EXPECT_EQ(expectedPaintRect, update.paintRects[0]);
335}
336
337TEST(PaintAggregator, OverlappingPaintAfterScroll)
338{
339    PaintAggregator greg;
340
341    IntRect scrollRect(0, 0, 10, 10);
342    greg.scrollRect(2, 0, scrollRect);
343
344    IntRect paintRect(4, 4, 10, 2);
345    greg.invalidateRect(paintRect);
346
347    IntRect expectedPaintRect = unionRect(scrollRect, paintRect);
348
349    EXPECT_TRUE(greg.hasPendingUpdate());
350    PaintAggregator::PendingUpdate update;
351    greg.popPendingUpdate(&update);
352
353    EXPECT_TRUE(update.scrollRect.isEmpty());
354    EXPECT_EQ(1U, update.paintRects.size());
355
356    EXPECT_EQ(expectedPaintRect, update.paintRects[0]);
357}
358
359TEST(PaintAggregator, DisjointPaintBeforeScroll)
360{
361    PaintAggregator greg;
362
363    IntRect paintRect(4, 4, 10, 2);
364    greg.invalidateRect(paintRect);
365
366    IntRect scrollRect(0, 0, 2, 10);
367    greg.scrollRect(2, 0, scrollRect);
368
369    EXPECT_TRUE(greg.hasPendingUpdate());
370    PaintAggregator::PendingUpdate update;
371    greg.popPendingUpdate(&update);
372
373    EXPECT_FALSE(update.scrollRect.isEmpty());
374    EXPECT_EQ(1U, update.paintRects.size());
375
376    EXPECT_EQ(paintRect, update.paintRects[0]);
377    EXPECT_EQ(scrollRect, update.scrollRect);
378}
379
380TEST(PaintAggregator, DisjointPaintAfterScroll)
381{
382    PaintAggregator greg;
383
384    IntRect scrollRect(0, 0, 2, 10);
385    greg.scrollRect(2, 0, scrollRect);
386
387    IntRect paintRect(4, 4, 10, 2);
388    greg.invalidateRect(paintRect);
389
390    EXPECT_TRUE(greg.hasPendingUpdate());
391    PaintAggregator::PendingUpdate update;
392    greg.popPendingUpdate(&update);
393
394    EXPECT_FALSE(update.scrollRect.isEmpty());
395    EXPECT_EQ(1U, update.paintRects.size());
396
397    EXPECT_EQ(paintRect, update.paintRects[0]);
398    EXPECT_EQ(scrollRect, update.scrollRect);
399}
400
401TEST(PaintAggregator, ContainedPaintTrimmedByScroll)
402{
403    PaintAggregator greg;
404
405    IntRect paintRect(4, 4, 6, 6);
406    greg.invalidateRect(paintRect);
407
408    IntRect scrollRect(0, 0, 10, 10);
409    greg.scrollRect(2, 0, scrollRect);
410
411    // The paint rect should have become narrower.
412    IntRect expectedPaintRect(6, 4, 4, 6);
413
414    EXPECT_TRUE(greg.hasPendingUpdate());
415    PaintAggregator::PendingUpdate update;
416    greg.popPendingUpdate(&update);
417
418    EXPECT_FALSE(update.scrollRect.isEmpty());
419    EXPECT_EQ(1U, update.paintRects.size());
420
421    EXPECT_EQ(expectedPaintRect, update.paintRects[0]);
422    EXPECT_EQ(scrollRect, update.scrollRect);
423}
424
425TEST(PaintAggregator, ContainedPaintEliminatedByScroll)
426{
427    PaintAggregator greg;
428
429    IntRect paintRect(4, 4, 6, 6);
430    greg.invalidateRect(paintRect);
431
432    IntRect scrollRect(0, 0, 10, 10);
433    greg.scrollRect(6, 0, scrollRect);
434
435    EXPECT_TRUE(greg.hasPendingUpdate());
436    PaintAggregator::PendingUpdate update;
437    greg.popPendingUpdate(&update);
438
439    EXPECT_FALSE(update.scrollRect.isEmpty());
440    EXPECT_TRUE(update.paintRects.isEmpty());
441
442    EXPECT_EQ(scrollRect, update.scrollRect);
443}
444
445TEST(PaintAggregator, ContainedPaintAfterScrollTrimmedByScrollDamage)
446{
447    PaintAggregator greg;
448
449    IntRect scrollRect(0, 0, 10, 10);
450    greg.scrollRect(4, 0, scrollRect);
451
452    IntRect paintRect(2, 0, 4, 10);
453    greg.invalidateRect(paintRect);
454
455    IntRect expectedScrollDamage(0, 0, 4, 10);
456    IntRect expectedPaintRect(4, 0, 2, 10);
457
458    EXPECT_TRUE(greg.hasPendingUpdate());
459    PaintAggregator::PendingUpdate update;
460    greg.popPendingUpdate(&update);
461
462    EXPECT_FALSE(update.scrollRect.isEmpty());
463    EXPECT_EQ(1U, update.paintRects.size());
464
465    EXPECT_EQ(scrollRect, update.scrollRect);
466    EXPECT_EQ(expectedScrollDamage, update.calculateScrollDamage());
467    EXPECT_EQ(expectedPaintRect, update.paintRects[0]);
468}
469
470TEST(PaintAggregator, ContainedPaintAfterScrollEliminatedByScrollDamage)
471{
472    PaintAggregator greg;
473
474    IntRect scrollRect(0, 0, 10, 10);
475    greg.scrollRect(4, 0, scrollRect);
476
477    IntRect paintRect(2, 0, 2, 10);
478    greg.invalidateRect(paintRect);
479
480    IntRect expectedScrollDamage(0, 0, 4, 10);
481
482    EXPECT_TRUE(greg.hasPendingUpdate());
483    PaintAggregator::PendingUpdate update;
484    greg.popPendingUpdate(&update);
485
486    EXPECT_FALSE(update.scrollRect.isEmpty());
487    EXPECT_TRUE(update.paintRects.isEmpty());
488
489    EXPECT_EQ(scrollRect, update.scrollRect);
490    EXPECT_EQ(expectedScrollDamage, update.calculateScrollDamage());
491}
492
493} // namespace
494