Bug 1498143 - Pass nodeActorID to screenshot actor to enable feature in iframes and shadowroots;r=pbro,yulia

Differential Revision: https://phabricator.services.mozilla.com/D12124

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Julian Descottes 2018-11-19 13:27:51 +00:00
Родитель 8e870bf61e
Коммит 6380b99f50
9 изменённых файлов: 213 добавлений и 2 удалений

Просмотреть файл

@ -2294,7 +2294,7 @@ Inspector.prototype = {
.getBoolPref("devtools.screenshot.clipboard.enabled");
const args = {
file: true,
selector: this.selectionCssSelector,
nodeActorID: this.selection.nodeFront.actorID,
clipboard: clipboardEnabled,
};
const screenshotFront = this.target.getFront("screenshot");

Просмотреть файл

@ -52,6 +52,7 @@ support-files =
helper_events_test_runner.js
helper_markup_accessibility_navigation.js
helper_outerhtml_test_runner.js
helper_screenshot_node.js
helper_style_attr_test_runner.js
lib_babel_6.21.0_min.js
lib_jquery_1.0.js
@ -170,6 +171,9 @@ skip-if = verify
[browser_markup_pagesize_02.js]
[browser_markup_pseudo_on_reload.js]
[browser_markup_remove_xul_attributes.js]
[browser_markup_screenshot_node.js]
[browser_markup_screenshot_node_iframe.js]
[browser_markup_screenshot_node_shadowdom.js]
[browser_markup_search_01.js]
[browser_markup_shadowdom.js]
[browser_markup_shadowdom_clickreveal.js]

Просмотреть файл

@ -0,0 +1,24 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* import-globals-from helper_screenshot_node.js */
"use strict";
loadHelperScript("helper_screenshot_node.js");
const TEST_URL = `data:text/html;charset=utf8,
<div id="blue-node" style="width:30px;height:30px;background:rgb(0, 0, 255)"></div>`;
// Test that the "Screenshot Node" feature works with a regular node in the main document.
add_task(async function() {
const { inspector, toolbox } = await openInspectorForURL(encodeURI(TEST_URL));
info("Select the blue node");
await selectNode("#blue-node", inspector);
info("Take a screenshot of the blue node and verify it looks as expected");
const blueScreenshot = await takeNodeScreenshot(inspector);
await assertSingleColorScreenshotImage(blueScreenshot, 30, 30, { r: 0, g: 0, b: 255 });
await toolbox.destroy();
});

Просмотреть файл

@ -0,0 +1,27 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* import-globals-from helper_screenshot_node.js */
"use strict";
loadHelperScript("helper_screenshot_node.js");
const TEST_URL = `data:text/html;charset=utf8,
<iframe
src="data:text/html;charset=utf8,
<div style='width:30px;height:30px;background:rgb(255, 0, 0)'></div>"></iframe>`;
// Test that the "Screenshot Node" feature works with a node inside an iframe.
add_task(async function() {
const { inspector, toolbox } = await openInspectorForURL(encodeURI(TEST_URL));
info("Select the red node");
const redNode = await getNodeFrontInFrame("div", "iframe", inspector);
await selectNode(redNode, inspector);
info("Take a screenshot of the red node and verify it looks as expected");
const redScreenshot = await takeNodeScreenshot(inspector);
await assertSingleColorScreenshotImage(redScreenshot, 30, 30, { r: 255, g: 0, b: 0 });
await toolbox.destroy();
});

Просмотреть файл

@ -0,0 +1,36 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* import-globals-from helper_screenshot_node.js */
"use strict";
loadHelperScript("helper_screenshot_node.js");
const TEST_URL = `data:text/html;charset=utf8,
<test-component></test-component>
<script>
'use strict';
customElements.define('test-component', class extends HTMLElement {
constructor() {
super();
let shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML =
'<div style="width:30px;height:30px;background:rgb(0, 128, 0)"></div>';
}
});
</script>`;
// Test that the "Screenshot Node" feature works with a node inside a shadow root.
add_task(async function() {
const { inspector, toolbox } = await openInspectorForURL(encodeURI(TEST_URL));
info("Select the green node");
const greenNode = await getNodeFrontInShadowDom("div", "test-component", inspector);
await selectNode(greenNode, inspector);
info("Take a screenshot of the green node and verify it looks as expected");
const greenScreenshot = await takeNodeScreenshot(inspector);
await assertSingleColorScreenshotImage(greenScreenshot, 30, 30, { r: 0, g: 128, b: 0 });
await toolbox.destroy();
});

Просмотреть файл

@ -0,0 +1,84 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
function colorAt(image, x, y) {
// Create a test canvas element.
const HTML_NS = "http://www.w3.org/1999/xhtml";
const canvas = document.createElementNS(HTML_NS, "canvas");
canvas.width = image.width;
canvas.height = image.height;
// Draw the image in the canvas
const context = canvas.getContext("2d");
context.drawImage(image, 0, 0, image.width, image.height);
// Return the color found at the provided x,y coordinates as a "r, g, b" string.
const [r, g, b] = context.getImageData(x, y, 1, 1).data;
return { r, g, b };
}
function waitUntilScreenshot() {
return new Promise(async function(resolve) {
const { Downloads } = require("resource://gre/modules/Downloads.jsm");
const list = await Downloads.getList(Downloads.ALL);
const view = {
onDownloadAdded: download => {
download.whenSucceeded().then(() => {
resolve(download.target.path);
list.removeView(view);
});
},
};
await list.addView(view);
});
}
async function resetDownloads() {
info("Reset downloads");
const { Downloads } = require("resource://gre/modules/Downloads.jsm");
const publicList = await Downloads.getList(Downloads.PUBLIC);
const downloads = await publicList.getAll();
for (const download of downloads) {
publicList.remove(download);
await download.finalize(true);
}
}
async function takeNodeScreenshot(inspector) {
// Cleanup all downloads at the end of the test.
registerCleanupFunction(resetDownloads);
info("Call screenshotNode() and wait until the screenshot is found in the Downloads");
const whenScreenshotSucceeded = waitUntilScreenshot();
inspector.screenshotNode();
const filePath = await whenScreenshotSucceeded;
info("Create an image using the downloaded fileas source");
const image = new Image();
image.src = OS.Path.toFileURI(filePath);
await once(image, "load");
return image;
}
/* exported takeNodeScreenshot */
/**
* Check that the provided image has the expected width, height, and color.
* NOTE: This test assumes that the image is only made of a single color and will only
* check one pixel.
*/
async function assertSingleColorScreenshotImage(image, width, height, { r, g, b }) {
is(image.width, width, "node screenshot has the expected width");
is(image.height, height, "node screenshot has the expected height");
const color = colorAt(image, 0, 0);
is(color.r, r, "node screenshot has the expected red component");
is(color.g, g, "node screenshot has the expected green component");
is(color.b, b, "node screenshot has the expected blue component");
}
/* exported assertSingleColorScreenshotImage */

Просмотреть файл

@ -236,6 +236,31 @@ var getNodeFrontInFrame = async function(selector, frameSelector,
return inspector.walker.querySelector(nodes[0], selector);
};
/**
* Get the NodeFront for a node that matches a given css selector inside a shadow root.
*
* @param {String} selector
* CSS selector of the node inside the shadow root.
* @param {String|NodeFront} hostSelector
* Selector or front of the element to which the shadow root is attached.
* @param {InspectorPanel} inspector
* The instance of InspectorPanel currently loaded in the toolbox
* @return {Promise} Resolves the node front when the inspector is updated with the new
* node.
*/
var getNodeFrontInShadowDom = async function(selector, hostSelector, inspector) {
const hostFront = await getNodeFront(hostSelector, inspector);
const {nodes} = await inspector.walker.children(hostFront);
// Find the shadow root in the children of the host element.
const shadowRoot = nodes.filter(node => node.isShadowRoot)[0];
if (!shadowRoot) {
throw new Error("Could not find a shadow root under selector: " + hostSelector);
}
return inspector.walker.querySelector(shadowRoot, selector);
};
var focusSearchBoxUsingShortcut = async function(panelWin, callback) {
info("Focusing search box");
const searchBox = panelWin.document.getElementById("inspector-searchbox");

Просмотреть файл

@ -15,6 +15,14 @@ exports.ScreenshotActor = protocol.ActorClassWithSpec(screenshotSpec, {
},
capture: function(args) {
if (args.nodeActorID) {
const nodeActor = this.conn.getActor(args.nodeActorID);
if (!nodeActor) {
throw new Error(
`Screenshot actor failed to find Node actor for '${args.nodeActorID}'`);
}
args.rawNode = nodeActor.rawNode;
}
return captureScreenshot(args, this.document);
},
});

Просмотреть файл

@ -48,7 +48,7 @@ exports.captureScreenshot = captureScreenshot;
* area of the browser window
*/
function createScreenshotDataURL(document, args) {
const window = document.defaultView;
let window = document.defaultView;
let left = 0;
let top = 0;
let width;
@ -65,6 +65,9 @@ function createScreenshotDataURL(document, args) {
width = window.innerWidth + window.scrollMaxX - window.scrollMinX;
height = window.innerHeight + window.scrollMaxY - window.scrollMinY;
filename = filename.replace(".png", "-fullpage.png");
} else if (args.rawNode) {
window = args.rawNode.ownerDocument.defaultView;
({ top, left, width, height } = getRect(window, args.rawNode, window));
} else if (args.selector) {
const node = window.document.querySelector(args.selector);
({ top, left, width, height } = getRect(window, node, window));