c252102e88277621e4fe7d6c585e4e7f1dd396ad
[WebKit-https.git] / LayoutTests / inspector / canvas / recording-2d.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <script src="../../http/tests/inspector/resources/inspector-test.js"></script>
5 <script src="resources/recording-utilities.js"></script>
6 <script>
7 let ctx = null;
8
9 // 2x2 red square
10 let image = document.createElement("img");
11 image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAAXNSR0IArs4c6QAAABNJREFUCB1j/M/AAEQMDEwgAgQAHxcCAmtAm/sAAAAASUVORK5CYII=";
12
13 // Invalid video
14 let video = document.createElement("video");
15
16 // Blank canvas
17 let canvas = document.createElement("canvas");
18 canvas.width = 2;
19 canvas.height = 2;
20
21 let linearGradient = null;
22
23 let radialGradient = null;
24
25 let pattern = null;
26
27 let path12 = new Path2D("M 1 2");
28 let path34 = new Path2D("M 3 4");
29
30 let imageData14 = new ImageData(1, 4);
31 let imageData23 = new ImageData(2, 3);
32
33 function load() {
34     ctx = canvas.getContext("2d");
35     linearGradient = ctx.createLinearGradient(1, 2, 3, 4);
36     radialGradient = ctx.createRadialGradient(1, 2, 3, 4, 5, 6);
37     pattern = ctx.createPattern(image, "no-repeat");
38
39     document.body.appendChild(canvas);
40
41     ctx.save();
42     ctx.save(); // This matches the `restore` call in `performActions`.
43
44     runTest();
45 }
46
47 function ignoreException(func){
48     try {
49         func();
50     } catch (e) { }
51 }
52
53 let timeoutID = NaN;
54 let restoreCalled = false;
55
56 function cancelActions() {
57     if (!isNaN(timeoutID)) {
58         ctx.restore();
59         if (!restoreCalled)
60             ctx.restore();
61     }
62
63     clearTimeout(timeoutID);
64     timeoutID = NaN;
65
66     ctx.save();
67     ctx.save();
68     ctx.resetTransform();
69     ctx.beginPath();
70     ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
71
72     restoreCalled = false;
73 }
74
75 function performActions() {
76     let frames = [
77         () => {
78             ignoreException(() => ctx.arc(1, 2, 3, 4, 5));
79             ignoreException(() => ctx.arc(6, 7, 8, 9, 10, true));
80         },
81         () => {
82             ignoreException(() => ctx.arcTo(1, 2, 3, 4, 5));
83         },
84         () => {
85             ctx.beginPath();
86         },
87         () => {
88             ctx.bezierCurveTo(1, 2, 3, 4, 5, 6);
89         },
90         () => {
91             ctx.clearRect(1, 2, 3, 4);
92         },
93         () => {
94             ctx.clearShadow();
95         },
96         () => {
97             ctx.clip();
98             ctx.clip("evenodd");
99             ctx.clip(path12);
100             ctx.clip(path34, "evenodd");
101         },
102         () => {
103             ctx.closePath();
104         },
105         () => {
106             ignoreException(() => ctx.createImageData(imageData14));
107             ignoreException(() => ctx.createImageData(2, 3));
108         },
109         () => {
110             ignoreException(() => ctx.createLinearGradient(1, 2, 3, 4));
111         },
112         () => {
113             ignoreException(() => ctx.createPattern(image, "testA"));
114             ignoreException(() => ctx.createPattern(video, "testB"));
115             ignoreException(() => ctx.createPattern(canvas, "testC"));
116         },
117         () => {
118             ignoreException(() => ctx.createRadialGradient(1, 2, 3, 4, 5, 6));
119         },
120         () => {
121             ctx.direction;
122             ctx.direction = "test";
123         },
124         () => {
125             ctx.drawFocusIfNeeded(document.body);
126             ctx.drawFocusIfNeeded(path12, document.body);
127         },
128         () => {
129             ignoreException(() => ctx.drawImage(image, 1, 2));
130             ignoreException(() => ctx.drawImage(video, 3, 4));
131             ignoreException(() => ctx.drawImage(canvas, 5, 6));
132             ignoreException(() => ctx.drawImage(image, 7, 8, 9, 10));
133             ignoreException(() => ctx.drawImage(video, 11, 12, 13, 14));
134             ignoreException(() => ctx.drawImage(canvas, 15, 16, 17, 18));
135             ignoreException(() => ctx.drawImage(image, 19, 20, 21, 22, 23, 24, 25, 26));
136             ignoreException(() => ctx.drawImage(video, 27, 28, 29, 30, 31, 32, 33, 34));
137             ignoreException(() => ctx.drawImage(canvas, 35, 36, 37, 38, 39, 40, 41 ,42));
138         },
139         () => {
140             ctx.drawImageFromRect(image, 1, 2, 3, 4, 5, 6, 7, 8)
141             ctx.drawImageFromRect(image, 9, 10, 11, 12, 13, 14, 15, 16, "test");
142         },
143         () => {
144             ignoreException(() => ctx.ellipse(1, 2, 3, 4, 5, 6, 7));
145             ignoreException(() => ctx.ellipse(8, 9, 10, 11, 12, 13, 14, true));
146         },
147         () => {
148             ctx.fill();
149             ctx.fill("evenodd");
150             ctx.fill(path12);
151             ctx.fill(path34, "evenodd");
152         },
153         () => {
154             ctx.fillRect(1, 2, 3, 4);
155         },
156         () => {
157             ctx.fillStyle;
158             ctx.fillStyle = "test";
159             ctx.fillStyle = linearGradient;
160             ctx.fillStyle = radialGradient;
161             ctx.fillStyle = pattern;
162         },
163         () => {
164             ctx.fillText("testA", 1, 2);
165             ctx.fillText("testB", 3, 4, 5);
166         },
167         () => {
168             ctx.font;
169             ctx.font = "test";
170         },
171         () => {
172             ignoreException(() => ctx.getImageData(1, 2, 3, 4));
173         },
174         () => {
175             ctx.getLineDash();
176         },
177         () => {
178             ctx.getTransform();
179         },
180         () => {
181             ctx.globalAlpha;
182             ctx.globalAlpha = 0;
183         },
184         () => {
185             ctx.globalCompositeOperation;
186             ctx.globalCompositeOperation = "test";
187         },
188         () => {
189             ctx.imageSmoothingEnabled;
190             ctx.imageSmoothingEnabled = true;
191         },
192         () => {
193             ctx.imageSmoothingQuality;
194             ctx.imageSmoothingQuality = "low";
195         },
196         () => {
197             ctx.isPointInPath(path12, 5, 6);
198             ctx.isPointInPath(path34, 7, 8, "evenodd");
199             ctx.isPointInPath(9, 10);
200             ctx.isPointInPath(11, 12, "evenodd");
201         },
202         () => {
203             ctx.isPointInStroke(path12, 3, 4);
204             ctx.isPointInStroke(5, 6);
205         },
206         () => {
207             ctx.lineCap;
208             ctx.lineCap = "test";
209         },
210         () => {
211             ctx.lineDashOffset;
212             ctx.lineDashOffset = 1;
213         },
214         () => {
215             ctx.lineJoin;
216             ctx.lineJoin = "test";
217         },
218         () => {
219             ctx.lineTo(1, 2);
220         },
221         () => {
222             ctx.lineWidth;
223             ctx.lineWidth = 1;
224         },
225         () => {
226             ctx.measureText("test");
227         },
228         () => {
229             ctx.miterLimit;
230             ctx.miterLimit = 1;
231         },
232         () => {
233             ctx.moveTo(1, 2);
234         },
235         () => {
236             ctx.putImageData(imageData14, 5, 6);
237             ctx.putImageData(imageData23, 7, 8, 9, 10, 11, 12);
238         },
239         () => {
240             ctx.quadraticCurveTo(1, 2, 3, 4);
241         },
242         () => {
243             ctx.rect(1, 2, 3, 4);
244         },
245         () => {
246             ctx.resetTransform();
247         },
248         () => {
249             ctx.restore();
250
251             restoreCalled = true;
252         },
253         () => {
254             ctx.rotate(1);
255         },
256         () => {
257             ctx.save();
258         },
259         () => {
260             ctx.scale(1, 2);
261         },
262         () => {
263             ctx.setAlpha();
264             ctx.setAlpha(1);
265         },
266         () => {
267             ctx.setCompositeOperation();
268             ctx.setCompositeOperation("test");
269         },
270         () => {
271             ctx.setFillColor("testA");
272             ctx.setFillColor("testB", 1);
273             ctx.setFillColor(2);
274             ctx.setFillColor(3, 4);
275             ctx.setFillColor(5, 6, 7, 8);
276             ctx.setFillColor(9, 10, 11, 12, 13);
277         },
278         () => {
279             ctx.setLineCap();
280             ctx.setLineCap("test");
281         },
282         () => {
283             ctx.setLineDash([1, 2]);
284         },
285         () => {
286             ctx.setLineJoin();
287             ctx.setLineJoin("test");
288         },
289         () => {
290             ctx.setLineWidth();
291             ctx.setLineWidth(1);
292         },
293         () => {
294             ctx.setMiterLimit();
295             ctx.setMiterLimit(1);
296         },
297         () => {
298             ctx.setShadow(1, 2, 3);
299             ctx.setShadow(4, 5, 6, "test", 7);
300             ctx.setShadow(8, 9, 10, 11);
301             ctx.setShadow(12, 13, 14, 15, 16);
302             ctx.setShadow(17, 18, 19, 20, 21, 22, 23);
303             ctx.setShadow(24, 25, 26, 27, 28, 29, 30, 31);
304         },
305         () => {
306             ctx.setStrokeColor("testA");
307             ctx.setStrokeColor("testB", 1);
308             ctx.setStrokeColor(2);
309             ctx.setStrokeColor(3, 4);
310             ctx.setStrokeColor(5, 6, 7, 8);
311             ctx.setStrokeColor(9, 10, 11, 12, 13);
312         },
313         () => {
314             ctx.setTransform(1, 2, 3, 4, 5, 6);
315             ignoreException(() => ctx.setTransform());
316             ignoreException(() => ctx.setTransform(new DOMMatrix([7, 8, 9, 10, 11, 12])));
317         },
318         () => {
319             ctx.shadowBlur;
320             ctx.shadowBlur = 1;
321         },
322         () => {
323             ctx.shadowColor;
324             ctx.shadowColor = "test";
325         },
326         () => {
327             ctx.shadowOffsetX;
328             ctx.shadowOffsetX = 1;
329         },
330         () => {
331             ctx.shadowOffsetY;
332             ctx.shadowOffsetY = 1;
333         },
334         () => {
335             ctx.stroke();
336             ctx.stroke(path12);
337         },
338         () => {
339             ctx.strokeRect(1, 2, 3, 4);
340         },
341         () => {
342             ctx.strokeStyle;
343             ctx.strokeStyle = "test";
344             ctx.strokeStyle = linearGradient;
345             ctx.strokeStyle = radialGradient;
346             ctx.strokeStyle = pattern;
347         },
348         () => {
349             ctx.strokeText("testA", 1, 2);
350             ctx.strokeText("testB", 3, 4, 5);
351         },
352         () => {
353             ctx.textAlign;
354             ctx.textAlign = "test";
355         },
356         () => {
357             ctx.textBaseline;
358             ctx.textBaseline = "test";
359         },
360         () => {
361             ctx.transform(1, 2, 3, 4, 5, 6);
362         },
363         () => {
364             ctx.translate(1, 2);
365         },
366         () => {
367             ignoreException(() => ctx.webkitGetImageDataHD(1, 2, 3, 4));
368         },
369         () => {
370             ctx.webkitImageSmoothingEnabled;
371             ctx.webkitImageSmoothingEnabled = true;
372         },
373         () => {
374             ctx.webkitLineDash;
375             ctx.webkitLineDash = [1, 2];
376         },
377         () => {
378             ctx.webkitLineDashOffset;
379             ctx.webkitLineDashOffset = 1;
380         },
381         () => {
382             ctx.webkitPutImageDataHD(imageData14, 5, 6);
383             ctx.webkitPutImageDataHD(imageData23, 7, 8, 9, 10, 11, 12);
384         },
385         () => {
386             TestPage.dispatchEventToFrontend("LastFrame");
387         },
388     ];
389     let index = 0;
390     function executeFrameFunction() {
391         frames[index++]();
392         if (index < frames.length)
393             timeoutID = setTimeout(executeFrameFunction, 0);
394     };
395     executeFrameFunction();
396 }
397
398 function performNaNActions() {
399     ctx.globalAlpha = NaN;
400 }
401
402 function test() {
403     let suite = InspectorTest.createAsyncSuite("Canvas.recording2D");
404
405     suite.addTestCase({
406         name: "Canvas.recording2D.singleFrame",
407         description: "Check that the recording is stopped after a single frame.",
408         test(resolve, reject) {
409             startRecording(WI.Canvas.ContextType.Canvas2D, resolve, reject, {singleFrame: true});
410         },
411     });
412
413     suite.addTestCase({
414         name: "Canvas.recording2D.multipleFrames",
415         description: "Check that recording data is serialized correctly for multiple frames.",
416         test(resolve, reject) {
417             let canvas = startRecording(WI.Canvas.ContextType.Canvas2D, resolve, reject, {singleFrame: false});
418
419             InspectorTest.singleFireEventListener("LastFrame", () => {
420                 CanvasAgent.stopRecording(canvas.identifier, (error) => {
421                     if (error) {
422                         reject(error);
423                         return;
424                     }
425                 });
426             });
427         },
428     });
429
430     suite.addTestCase({
431         name: "Canvas.recording2D.memoryLimit",
432         description: "Check that the recording is stopped when it reaches the memory limit.",
433         test(resolve, reject) {
434             startRecording(WI.Canvas.ContextType.Canvas2D, resolve, reject, {memoryLimit: 10});
435         },
436     });
437
438     suite.addTestCase({
439         name: "Canvas.recording2D.ActionParameterNaN",
440         description: "Check that NaN is converted into the proper value for serialization.",
441         test(resolve, reject) {
442             let canvas = getCanvas(WI.Canvas.ContextType.Canvas2D);
443             if (!canvas) {
444                 reject("Missing 2D canvas.");
445                 return;
446             }
447
448             WI.canvasManager.awaitEvent(WI.CanvasManager.Event.RecordingStopped)
449             .then((event) => {
450                 let recording = event.data.recording.toJSON();
451
452                 let frames = recording.frames;
453                 InspectorTest.expectEqual(frames.length, 1, "The recording should have 1 frame.");
454
455                 let actions = frames[0].actions;
456                 InspectorTest.expectEqual(actions.length, 1, "The first frame should have 1 action.");
457                 InspectorTest.expectEqual(actions[0][1].length, 1, "The action should have 1 parameter.");
458                 InspectorTest.expectEqual(actions[0][1][0], null, "The parameter should be null.");
459             })
460             .then(resolve, reject);
461
462             const singleFrame = true;
463             CanvasAgent.startRecording(canvas.identifier, singleFrame, (error) => {
464                 if (error) {
465                     reject(error);
466                     return;
467                 }
468
469                 InspectorTest.evaluateInPage(`performNaNActions()`);
470             });
471         },
472     });
473
474     suite.addTestCase({
475         name: "Canvas.recording2D.NoActions",
476         description: "Check that a canvas is still able to be recorded after stopping a recording with no actions.",
477         test(resolve, reject) {
478             let canvas = getCanvas(WI.Canvas.ContextType.Canvas2D);
479             if (!canvas)
480                 throw "Missing 2D canvas.";
481
482             let eventCount = 0;
483             function handleRecordingStopped(event) {
484                 InspectorTest.assert(event.data.canvas === canvas, "We should have stopped recording the selected canvas.");
485                 ++eventCount;
486
487                 if (eventCount == 1)
488                     InspectorTest.pass("A recording should have been started and stopped once.");
489
490                 if (eventCount >= 2) {
491                     InspectorTest.pass("A recording should have been started and stopped twice.");
492
493                     WI.canvasManager.removeEventListener(handleRecordingStopped);
494                     resolve();
495                 }
496             }
497             WI.canvasManager.addEventListener(WI.CanvasManager.Event.RecordingStopped, handleRecordingStopped);
498
499             WI.canvasManager.startRecording(canvas);
500             WI.canvasManager.stopRecording();
501
502             WI.canvasManager.startRecording(canvas);
503             WI.canvasManager.stopRecording();
504         },
505     });
506
507     suite.runTestCasesAndFinish();
508 }
509 </script>
510 </head>
511 <body onload="load()">
512     <p>Test that CanvasManager is able to record actions made to 2D canvas contexts.</p>
513 </body>
514 </html>