Index: chrome/test/data/extensions/api_test/tab_capture/presentation.js |
diff --git a/chrome/test/data/extensions/api_test/tab_capture/presentation.js b/chrome/test/data/extensions/api_test/tab_capture/presentation.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f4e0cb5fe103f4129070fe940148439371f76111 |
--- /dev/null |
+++ b/chrome/test/data/extensions/api_test/tab_capture/presentation.js |
@@ -0,0 +1,172 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+// Tests that offscreen presentations can be created and captured. The |
+// offscreen presentation page generates video test patterns that rotate |
+// cyclicly, and the rendering output of the tab is captured into a |
+// LocalMediaStream. This stream is then piped into a video element for |
+// playback, and a canvas is used to examine the frames of the video for |
+// expected content. |
+ |
+// The offscreen page content for this test is provided as a data URI. |
+var offscreenPageUri = 'data:text/html;charset=UTF-8,' + encodeURIComponent('\n\ |
+<html><body></body><script>\n\ |
+// The test pattern cycles as a color fill of red, then green, then blue.\n\ |
+var colors = [ [ 255, 0, 0 ], [ 0, 255, 0 ], [ 0, 0, 255 ] ];\n\ |
+var curIdx = 0;\n\ |
+\n\ |
+function updateTestPattern() {\n\ |
+ if (!this.canvas) {\n\ |
+ this.canvas = document.createElement("canvas");\n\ |
+ this.canvas.width = 320;\n\ |
+ this.canvas.height = 200;\n\ |
+ this.canvas.style.position = "absolute";\n\ |
+ this.canvas.style.top = "0px";\n\ |
+ this.canvas.style.left = "0px";\n\ |
+ this.canvas.style.width = "100%";\n\ |
+ this.canvas.style.height = "100%";\n\ |
+ document.body.appendChild(this.canvas);\n\ |
+ }\n\ |
+ var context = this.canvas.getContext("2d");\n\ |
+ // Fill with solid color.\n\ |
+ context.fillStyle = "rgb(" + colors[curIdx] + ")";\n\ |
+ context.fillRect(0, 0, this.canvas.width, this.canvas.height);\n\ |
+ // Draw the circle that moves around the page.\n\ |
+ context.fillStyle = "rgb(" + colors[(curIdx + 1) % colors.length] + ")";\n\ |
+ context.beginPath();\n\ |
+ if (!this.frameNumber) {\n\ |
+ this.frameNumber = 1;\n\ |
+ } else {\n\ |
+ ++this.frameNumber;\n\ |
+ }\n\ |
+ var i = this.frameNumber % 200;\n\ |
+ var t = (this.frameNumber + 3000) * (0.01 + i / 8000.0);\n\ |
+ var x = (Math.sin(t) * 0.45 + 0.5) * this.canvas.width;\n\ |
+ var y = (Math.cos(t * 0.9) * 0.45 + 0.5) * this.canvas.height;\n\ |
+ context.arc(x, y, 16, 0, 2 * Math.PI, false);\n\ |
+ context.closePath();\n\ |
+ context.fill();\n\ |
+}\n\ |
+\n\ |
+function renderTestPatternLoop() {\n\ |
+ requestAnimationFrame(renderTestPatternLoop);\n\ |
+ updateTestPattern();\n\ |
+\n\ |
+ if (!this.stepTimeMillis) {\n\ |
+ this.stepTimeMillis = 100;\n\ |
+ }\n\ |
+ var now = new Date().getTime();\n\ |
+ if (!this.nextSteppingAt) {\n\ |
+ this.nextSteppingAt = now + this.stepTimeMillis;\n\ |
+ } else if (now >= this.nextSteppingAt) {\n\ |
+ ++curIdx;\n\ |
+ if (curIdx >= colors.length) { // Completed a cycle.\n\ |
+ curIdx = 0;\n\ |
+ // Increase the wait time between switching test patterns for\n\ |
+ // overloaded bots that are not capturing all the frames of video.\n\ |
+ this.stepTimeMillis *= 1.25;\n\ |
+ }\n\ |
+ this.nextSteppingAt = now + this.stepTimeMillis;\n\ |
+ }\n\ |
+}\n\ |
+\n\ |
+renderTestPatternLoop();\n\ |
+</script></body></html>'); |
+ |
+// Capture parameters. |
+var width = 64; |
+var height = 48; |
+var frameRate = 15; |
+ |
+// The stream to playback in the video element. |
+var receiveStream = null; |
+ |
+// waitForExpectedColors() removes elements from this array as each is observed. |
+// When it becomes empty, the test succeeds. |
+var expectedColors = [ [ 255, 0, 0 ], [ 0, 255, 0 ], [ 0, 0, 255 ] ]; |
+ |
+function waitForExpectedColors(colorDeviation) { |
+ // If needed, create the video and canvas elements, but no need to append them |
+ // to the DOM. |
+ if (!this.video) { |
+ this.video = document.createElement("video"); |
+ this.video.width = width; |
+ this.video.height = height; |
+ this.video.addEventListener("error", chrome.test.fail); |
+ this.video.src = URL.createObjectURL(receiveStream); |
+ this.video.play(); |
+ |
+ this.readbackCanvas = document.createElement("canvas"); |
+ this.readbackCanvas.width = width; |
+ this.readbackCanvas.height = height; |
+ } |
+ |
+ // Only bother examining a video frame if the video timestamp has advanced. |
+ var currentVideoTimestamp = this.video.currentTime; |
+ if (!this.lastVideoTimestamp || |
+ this.lastVideoTimestamp < currentVideoTimestamp) { |
+ this.lastVideoTimestamp = currentVideoTimestamp; |
+ |
+ // Grab a snapshot of the center pixel of the video. |
+ var ctx = this.readbackCanvas.getContext("2d"); |
+ ctx.drawImage(video, 0, 0, width, height); |
+ var imageData = ctx.getImageData(width / 2, height / 2, 1, 1); |
+ var pixel = [ imageData.data[0], imageData.data[1], imageData.data[2] ]; |
+ |
+ // Does the pixel match one of the expected colors? |
+ for (var i = 0; i < expectedColors.length; ++i) { |
+ var curColor = expectedColors[i]; |
+ if (Math.abs(pixel[0] - curColor[0]) <= colorDeviation && |
+ Math.abs(pixel[1] - curColor[1]) <= colorDeviation && |
+ Math.abs(pixel[2] - curColor[2]) <= colorDeviation) { |
+ console.debug("Observed expected color RGB(" + curColor + |
+ ") in the video as RGB(" + pixel + ")"); |
+ expectedColors.splice(i, 1); |
+ } |
+ } |
+ } |
+ |
+ if (expectedColors.length == 0) { |
+ chrome.test.succeed(); |
+ } else { |
+ setTimeout(function () { waitForExpectedColors(colorDeviation); }, |
+ 1000 / frameRate); |
+ } |
+} |
+ |
+chrome.test.runTests([ |
+ function presentationTest() { |
+ // Parse colorDeviation query parameter. |
+ var colorDeviation; // How far from the expected intensity ([0,255] scale)? |
+ try { |
+ colorDeviation = parseInt( |
+ window.location.search.match(/(\?|&)colorDeviation=(\d+)/)[2]); |
+ chrome.test.assertTrue(colorDeviation >= 0 && colorDeviation <= 255); |
+ } catch (err) { |
+ chrome.test.fail("Error parsing query params -- " + err.message); |
+ return; |
+ } |
+ |
+ chrome.tabCapture.capturePresentation( |
+ offscreenPageUri, |
+ "bubbles", |
+ { video: true, |
+ audio: false, |
+ videoConstraints: { |
+ mandatory: { |
+ minWidth: width, |
+ minHeight: height, |
+ maxWidth: width, |
+ maxHeight: height, |
+ maxFrameRate: frameRate, |
+ } |
+ } |
+ }, |
+ function examineTheOffscreenCaptureVideo(captureStream) { |
+ chrome.test.assertTrue(!!captureStream); |
+ receiveStream = captureStream; |
+ waitForExpectedColors(colorDeviation); |
+ }); |
+ } |
+]); |