1# Copyright 2014 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import re
6
7
8def result_contains_repaint_rects(text):
9    return isinstance(text, str) and (
10        re.search('"repaintRects": \[$', text, re.MULTILINE) != None or
11        text.find('Minimum repaint:') != -1)
12
13
14def extract_layer_tree(input_str):
15    if not isinstance(input_str, str):
16        return '{}'
17
18    if input_str[0:2] == '{\n':
19        start = 0
20    else:
21        start = input_str.find('\n{\n')
22        if start == -1:
23            return '{}'
24
25    end = input_str.find('\n}\n', start)
26    if end == -1:
27        return '{}'
28
29    # FIXME: There may be multiple layer trees in the result.
30    return input_str[start:end + 3]
31
32
33def generate_repaint_overlay_html(test_name, actual_text, expected_text):
34    if not result_contains_repaint_rects(actual_text) and not result_contains_repaint_rects(expected_text):
35        return ''
36
37    expected_layer_tree = extract_layer_tree(expected_text)
38    actual_layer_tree = extract_layer_tree(actual_text)
39
40    minimum_repaint = '[]'
41    minimum_repaint_match = re.search('Minimum repaint:\n(\[.*\n\])', actual_text, re.DOTALL)
42    if minimum_repaint_match:
43        minimum_repaint = minimum_repaint_match.group(1)
44
45    return """<!DOCTYPE HTML>
46<html>
47<head>
48<title>%(title)s</title>
49<style>
50    body {
51        margin: 0;
52        padding: 0;
53    }
54    iframe {
55      position: absolute;
56      top: 80px;
57      left: 0;
58      border: 0;
59      z-index: -1;
60    }
61    canvas {
62      position: absolute;
63      top: 80px;
64      left: 0;
65      z-index: 1;
66    }
67    #actual, #minimum-repaint {
68      display: none;
69    }
70</style>
71</head>
72<body>
73<a href="http://crbug.com/381221">Known issues</a><br>
74<label><input id="show-test" type="checkbox" checked onchange="toggle_test(this.checked)">Show test</label>
75<label title="See fast/repaint/resources/text-based-repaint.js for how this works">
76    <input id="show-minimum-repaint" type="checkbox" onchange="toggle_minimum_repaint(this.checked)">Minimum repaint
77</label>
78<label><input id="use-solid-colors" type="checkbox" onchange="toggle_solid_color(this.checked)">Use solid colors</label>
79<br>
80<button title="See fast/repaint/resources/text-based-repaint.js for how this works" onclick="highlight_under_repaint()">
81    Highlight under-repaint
82</button>
83<br>
84<span id='type'>Expected Invalidations</span>
85<div id=overlay>
86    <canvas id='minimum-repaint' width='2000' height='2000'></canvas>
87    <canvas id='expected' width='2000' height='2000'></canvas>
88    <canvas id='actual' width='2000' height='2000'></canvas>
89</div>
90<script>
91var overlay_opacity = 0.25;
92
93function toggle_test(show_test) {
94    iframe.style.display = show_test ? 'block' : 'none';
95}
96
97function toggle_minimum_repaint(show_minimum_repaint) {
98    document.getElementById('minimum-repaint').style.display = show_minimum_repaint ? 'block' : 'none';
99}
100
101function toggle_solid_color(use_solid_color) {
102    overlay_opacity = use_solid_color ? 1 : 0.25;
103    draw_repaint_rects();
104    draw_minimum_repaint();
105}
106
107function highlight_under_repaint() {
108    document.getElementById('show-test').checked = false;
109    toggle_test(false);
110    document.getElementById('show-minimum-repaint').checked = true;
111    toggle_minimum_repaint(true);
112    document.getElementById('use-solid-colors').checked = true;
113    toggle_solid_color(true);
114}
115
116var expected = %(expected)s;
117var actual = %(actual)s;
118var minimum_repaint = %(minimum_repaint)s;
119
120function rectsEqual(rect1, rect2) {
121    return rect1[0] == rect2[0] && rect1[1] == rect2[1] && rect1[2] == rect2[2] && rect1[3] == rect2[3];
122}
123
124function draw_rects(context, rects) {
125    for (var i = 0; i < rects.length; ++i) {
126        var rect = rects[i];
127        context.fillRect(rect[0], rect[1], rect[2], rect[3]);
128    }
129}
130
131function draw_layer_rects(context, result) {
132    context.save();
133    if (result.position)
134        context.translate(result.position[0], result.position[1]);
135    var t = result.transform;
136    if (t) {
137        var origin = result.transformOrigin || [result.bounds[0] / 2, result.bounds[1] / 2];
138        context.translate(origin[0], origin[1]);
139        context.transform(t[0][0], t[0][1], t[1][0], t[1][1], t[3][0], t[3][1]);
140        context.translate(-origin[0], -origin[1]);
141    }
142    if (result.repaintRects)
143        draw_rects(context, result.repaintRects);
144    if (result.children) {
145        for (var i = 0; i < result.children.length; ++i)
146            draw_layer_rects(context, result.children[i]);
147    }
148    context.restore();
149}
150
151var expected_canvas = document.getElementById('expected');
152var actual_canvas = document.getElementById('actual');
153var minimum_repaint_canvas = document.getElementById('minimum-repaint');
154
155function draw_repaint_rects() {
156    var expected_ctx = expected_canvas.getContext("2d");
157    expected_ctx.clearRect(0, 0, 2000, 2000);
158    expected_ctx.fillStyle = 'rgba(255, 0, 0, ' + overlay_opacity + ')';
159    draw_layer_rects(expected_ctx, expected);
160
161    var actual_ctx = actual_canvas.getContext("2d");
162    actual_ctx.clearRect(0, 0, 2000, 2000);
163    actual_ctx.fillStyle = 'rgba(0, 255, 0, ' + overlay_opacity + ')';
164    draw_layer_rects(actual_ctx, actual);
165}
166
167function draw_minimum_repaint() {
168    var context = minimum_repaint_canvas.getContext("2d");
169    context.fillStyle = 'rgba(0, 0, 0, 1)';
170    draw_rects(context, minimum_repaint);
171}
172
173draw_repaint_rects();
174draw_minimum_repaint();
175
176var path = decodeURIComponent(location.search).substr(1);
177var iframe = document.createElement('iframe');
178iframe.id = 'test-frame';
179iframe.width = 800;
180iframe.height = 600;
181iframe.src = path;
182
183var overlay = document.getElementById('overlay');
184overlay.appendChild(iframe);
185
186var type = document.getElementById('type');
187var expected_showing = true;
188function flip() {
189    if (expected_showing) {
190        type.textContent = 'Actual Invalidations';
191        expected_canvas.style.display = 'none';
192        actual_canvas.style.display = 'block';
193    } else {
194        type.textContent = 'Expected Invalidations';
195        actual_canvas.style.display = 'none';
196        expected_canvas.style.display = 'block';
197    }
198    expected_showing = !expected_showing
199}
200setInterval(flip, 3000);
201</script>
202</body>
203</html>
204""" % {
205        'title': test_name,
206        'expected': expected_layer_tree,
207        'actual': actual_layer_tree,
208        'minimum_repaint': minimum_repaint,
209    }
210