Implemented headless renderer graphics tests.

Change-Id: Iad9f67766976f8c60636321fbaea9c6ede8a1fce
Reviewed-on: https://chromium-review.googlesource.com/1150633
Reviewed-by: Andrey Kosyakov <caseq@chromium.org>
Commit-Queue: Peter Kvitek <kvitekp@chromium.org>
Cr-Commit-Position: refs/heads/master@{#578072}
diff --git a/headless/test/data/protocol/helpers/virtual-time-controller.js b/headless/test/data/protocol/helpers/virtual-time-controller.js
index 45482489..92cfa27 100644
--- a/headless/test/data/protocol/helpers/virtual-time-controller.js
+++ b/headless/test/data/protocol/helpers/virtual-time-controller.js
@@ -74,7 +74,15 @@
   }
 
   /**
-   * Revokes any granted virtual time, resulting in no more animatino frames
+   * Retrieves current frame time to be used in beginFrame calls.
+   * @return {number} Frame time in milliseconds.
+   */
+  currentFrameTime() {
+    return this.virtualTimeBase_ + this.totalElapsedTime_;
+  }
+
+  /**
+   * Revokes any granted virtual time, resulting in no more animation frames
    * being issued and final OnExpired call being made.
    */
   stopVirtualTimeGracefully() {
diff --git a/headless/test/data/protocol/sanity/renderer-canvas-expected.txt b/headless/test/data/protocol/sanity/renderer-canvas-expected.txt
new file mode 100644
index 0000000..3742fdf
--- /dev/null
+++ b/headless/test/data/protocol/sanity/renderer-canvas-expected.txt
@@ -0,0 +1,5 @@
+Tests renderer: canvas.
+requested url: http://example.com/
+Screenshot size: 800 x 600
+rgba @(0,0): 255,255,255,255
+rgba @(25,25): 255,0,0,255
\ No newline at end of file
diff --git a/headless/test/data/protocol/sanity/renderer-canvas.js b/headless/test/data/protocol/sanity/renderer-canvas.js
new file mode 100644
index 0000000..3f6d5145
--- /dev/null
+++ b/headless/test/data/protocol/sanity/renderer-canvas.js
@@ -0,0 +1,82 @@
+// Copyright 2018 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.
+
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank(
+      'Tests renderer: canvas.');
+
+  // Open the test page in a new tab with BeginFrameControl enabled.
+  await testTargetPage(await session.createTargetInNewContext(
+      800, 600, 'about:blank', true));
+
+  async function testTargetPage(session) {
+    dp = session.protocol;
+    await dp.Runtime.enable();
+    await dp.HeadlessExperimental.enable();
+
+    let RendererTestHelper =
+        await testRunner.loadScript('../helpers/renderer-test-helper.js');
+    let {httpInterceptor, frameNavigationHelper, virtualTimeController} =
+        await (new RendererTestHelper(testRunner, dp, page)).init();
+
+    httpInterceptor.addResponse(
+        `http://example.com/`,
+        `<html>
+          <body>
+            <canvas id="test_canvas" width="100" height="100"
+                    style="position:absolute;left:0px;top:0px">
+              Oops!  Canvas not supported!
+            </canvas>
+            <script>
+              var context = document.getElementById("test_canvas").
+                            getContext("2d");
+              context.fillStyle = "rgb(255,0,0)";
+              context.fillRect(25, 25, 50, 50);
+            </script>
+          </body>
+        </html>`);
+
+    await virtualTimeController.grantInitialTime(500, 1000,
+      null,
+      async () => {
+        const frameTimeTicks = virtualTimeController.currentFrameTime();
+        const screenshotData =
+            (await dp.HeadlessExperimental.beginFrame(
+                {frameTimeTicks, screenshot: {format: 'png'}}))
+            .result.screenshotData;
+        await logScreenShotData(screenshotData);
+        testRunner.completeTest();
+      }
+    );
+
+    function logScreenShotData(pngBase64) {
+      const image = new Image();
+
+      let callback;
+      let promise = new Promise(fulfill => callback = fulfill);
+      image.onload = function() {
+        testRunner.log(`Screenshot size: `
+            + `${image.naturalWidth} x ${image.naturalHeight}`);
+        const canvas = document.createElement('canvas');
+        canvas.width = image.naturalWidth;
+        canvas.height = image.naturalHeight;
+        const ctx = canvas.getContext('2d');
+        ctx.drawImage(image, 0, 0);
+        // Expected rgba @(0,0): 255,255,255,255
+        const rgba = ctx.getImageData(0, 0, 1, 1).data;
+        testRunner.log(`rgba @(0,0): ${rgba}`);
+        // Expected rgba @(25,25): 255,0,0,255
+        const rgba2 = ctx.getImageData(25, 25, 1, 1).data;
+        testRunner.log(`rgba @(25,25): ${rgba2}`);
+        callback();
+      }
+
+      image.src = `data:image/png;base64,${pngBase64}`;
+
+      return promise;
+    }
+
+    await frameNavigationHelper.navigate('http://example.com/');
+  }
+})
diff --git a/headless/test/data/protocol/sanity/renderer-css-url-filter-expected.txt b/headless/test/data/protocol/sanity/renderer-css-url-filter-expected.txt
new file mode 100644
index 0000000..b8e97a0
--- /dev/null
+++ b/headless/test/data/protocol/sanity/renderer-css-url-filter-expected.txt
@@ -0,0 +1,30 @@
+Tests renderer: canvas.
+requested url: http://www.example.com/
+requested url: http://www.example.com/circle.svg
+requested url: http://www.example.com/blur.svg
+Screenshot size: 100 x 100
+rgba @(0,0): 163,163,163,255
+rgba @(1,1): 222,225,231,255
+rgba @(2,2): 221,232,251,255
+rgba @(3,3): 206,222,249,255
+rgba @(4,4): 255,255,255,255
+rgba @(5,5): 255,255,255,255
+rgba @(6,6): 201,218,245,255
+rgba @(7,7): 198,216,244,255
+rgba @(8,8): 197,214,243,255
+rgba @(9,9): 191,210,236,255
+rgba @(10,10): 152,194,170,255
+rgba @(11,11): 174,215,161,255
+rgba @(12,12): 255,255,255,255
+rgba @(13,13): 135,191,136,255
+rgba @(14,14): 141,190,140,255
+rgba @(15,15): 138,139,139,255
+rgba @(16,16): 255,255,255,255
+rgba @(17,17): 255,255,255,255
+rgba @(18,18): 255,255,255,255
+rgba @(19,19): 255,255,255,255
+rgba @(20,20): 255,255,255,255
+rgba @(21,21): 255,255,255,255
+rgba @(22,22): 255,255,255,255
+rgba @(23,23): 255,255,255,255
+rgba @(24,24): 255,255,255,255
\ No newline at end of file
diff --git a/headless/test/data/protocol/sanity/renderer-css-url-filter.js b/headless/test/data/protocol/sanity/renderer-css-url-filter.js
new file mode 100644
index 0000000..72a1a49
--- /dev/null
+++ b/headless/test/data/protocol/sanity/renderer-css-url-filter.js
@@ -0,0 +1,99 @@
+// Copyright 2018 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.
+
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank(
+      'Tests renderer: canvas.');
+
+  // Open the test page in a new tab with BeginFrameControl enabled.
+  await testTargetPage(await session.createTargetInNewContext(
+      100, 100, 'about:blank', true));
+
+  // Ensures that "filter: url(...)" does not get into an infinite style update
+  // loop.
+  async function testTargetPage(session) {
+    dp = session.protocol;
+    await dp.Runtime.enable();
+    await dp.HeadlessExperimental.enable();
+
+    let RendererTestHelper =
+        await testRunner.loadScript('../helpers/renderer-test-helper.js');
+    let {httpInterceptor, frameNavigationHelper, virtualTimeController} =
+        await (new RendererTestHelper(testRunner, dp, page)).init();
+
+    // The image from circle.svg will be drawn with the blur from blur.svg.
+    httpInterceptor.addResponse(
+        `http://www.example.com/`,
+        `<!DOCTYPE html>
+        <style>
+          body { margin: 0; }
+          img {
+            -webkit-filter: url(blur.svg#blur);
+            filter: url(blur.svg#blur);
+          }
+        </style>
+        <img src="circle.svg">`);
+
+    // Just a normal image.
+    httpInterceptor.addResponse(
+        `http://www.example.com/circle.svg`,
+        `<svg width="100" height="100" version="1.1"
+            xmlns="http://www.w3.org/2000/svg"
+          xmlns:xlink="http://www.w3.org/1999/xlink">
+          <circle cx="50" cy="50" r="50" fill="green" />
+        </svg>`);
+
+    // A blur filter stored inside an svg file.
+    httpInterceptor.addResponse(
+        `http://www.example.com/blur.svg`,
+        `<svg width="100" height="100" version="1.1"
+            xmlns="http://www.w3.org/2000/svg"
+            xmlns:xlink="http://www.w3.org/1999/xlink">
+          <filter id="blur">
+            <feGaussianBlur in="SourceGraphic" stdDeviation="5"/>
+          </filter>
+        </svg>`);
+
+    await virtualTimeController.grantInitialTime(500, 1000,
+      null,
+      async () => {
+        const frameTimeTicks = virtualTimeController.currentFrameTime();
+        const screenshotData =
+            (await dp.HeadlessExperimental.beginFrame(
+                {frameTimeTicks, screenshot: {format: 'png'}}))
+            .result.screenshotData;
+        await logScreenShotData(screenshotData);
+        testRunner.completeTest();
+      }
+    );
+
+    function logScreenShotData(pngBase64) {
+      const image = new Image();
+
+      let callback;
+      let promise = new Promise(fulfill => callback = fulfill);
+      image.onload = function() {
+        testRunner.log(`Screenshot size: `
+            + `${image.naturalWidth} x ${image.naturalHeight}`);
+        const canvas = document.createElement('canvas');
+        canvas.width = image.naturalWidth;
+        canvas.height = image.naturalHeight;
+        const ctx = canvas.getContext('2d');
+        ctx.drawImage(image, 0, 0);
+        for (let n = 0; n < 25; ++n) {
+          const rgba = ctx.getImageData(n, n, 1, 1).data;
+          testRunner.log(`rgba @(${n},${n}): ${rgba}`);
+        }
+
+        callback();
+      }
+
+      image.src = `data:image/png;base64,${pngBase64}`;
+
+      return promise;
+    }
+
+    await frameNavigationHelper.navigate('http://www.example.com/');
+  }
+})
diff --git a/headless/test/headless_protocol_browsertest.cc b/headless/test/headless_protocol_browsertest.cc
index f6b38e9..b609778 100644
--- a/headless/test/headless_protocol_browsertest.cc
+++ b/headless/test/headless_protocol_browsertest.cc
@@ -341,5 +341,8 @@
 HEADLESS_PROTOCOL_COMPOSITOR_TEST(
     RendererClientRedirectChainNoJs,
     "sanity/renderer-client-redirect-chain-no-js.js");
+HEADLESS_PROTOCOL_COMPOSITOR_TEST(RendererCssUrlFilter,
+                                  "sanity/renderer-css-url-filter.js");
+HEADLESS_PROTOCOL_COMPOSITOR_TEST(RendererCanvas, "sanity/renderer-canvas.js");
 
 }  // namespace headless