279 lines
8.5 KiB
JavaScript
279 lines
8.5 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
/* eslint no-unused-vars: [2, {"vars": "local"}] */
|
|
|
|
"use strict";
|
|
|
|
// Import the inspector's head.js first (which itself imports shared-head.js).
|
|
Services.scriptloader.loadSubScript(
|
|
"chrome://mochitests/content/browser/devtools/client/inspector/test/head.js",
|
|
this
|
|
);
|
|
|
|
registerCleanupFunction(() => {
|
|
Services.prefs.clearUserPref("devtools.defaultColorUnit");
|
|
});
|
|
|
|
/**
|
|
* Dispatch the copy event on the given element
|
|
*/
|
|
function fireCopyEvent(element) {
|
|
const evt = element.ownerDocument.createEvent("Event");
|
|
evt.initEvent("copy", true, true);
|
|
element.dispatchEvent(evt);
|
|
}
|
|
|
|
/**
|
|
* Return all the computed items in the computed view
|
|
*
|
|
* @param {CssComputedView} view
|
|
* The instance of the computed view panel
|
|
* @returns {Array<Element>}
|
|
*/
|
|
function getComputedViewProperties(view) {
|
|
return Array.from(
|
|
view.styleDocument.querySelectorAll(
|
|
"#computed-container .computed-property-view"
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get references to the name and value span nodes corresponding to a given
|
|
* property name in the computed-view
|
|
*
|
|
* @param {CssComputedView} view
|
|
* The instance of the computed view panel
|
|
* @param {String} name
|
|
* The name of the property to retrieve
|
|
* @return an object {nameSpan, valueSpan}
|
|
*/
|
|
function getComputedViewProperty(view, name) {
|
|
let prop;
|
|
for (const property of getComputedViewProperties(view)) {
|
|
const nameSpan = property.querySelector(".computed-property-name");
|
|
const valueSpan = property.querySelector(".computed-property-value");
|
|
|
|
if (nameSpan.firstChild.textContent === name) {
|
|
prop = { nameSpan, valueSpan };
|
|
break;
|
|
}
|
|
}
|
|
return prop;
|
|
}
|
|
|
|
/**
|
|
* Get an instance of PropertyView from the computed-view.
|
|
*
|
|
* @param {CssComputedView} view
|
|
* The instance of the computed view panel
|
|
* @param {String} name
|
|
* The name of the property to retrieve
|
|
* @return {PropertyView}
|
|
*/
|
|
function getComputedViewPropertyView(view, name) {
|
|
let propView;
|
|
for (const propertyView of view.propertyViews) {
|
|
if (propertyView.propertyInfo.name === name) {
|
|
propView = propertyView;
|
|
break;
|
|
}
|
|
}
|
|
return propView;
|
|
}
|
|
|
|
/**
|
|
* Get a reference to the matched rules element for a given property name in
|
|
* the computed-view.
|
|
* A matched rule element is inside the property element (<li>) itself
|
|
* and is only shown when the twisty icon is expanded on the property.
|
|
* It contains matched rules, with selectors, properties, values and stylesheet links.
|
|
*
|
|
* @param {CssComputedView} view
|
|
* The instance of the computed view panel
|
|
* @param {String} name
|
|
* The name of the property to retrieve
|
|
* @return {Promise} A promise that resolves to the property matched rules
|
|
* container
|
|
*/
|
|
var getComputedViewMatchedRules = async function (view, name) {
|
|
let expander;
|
|
let matchedRulesEl;
|
|
for (const property of view.styleDocument.querySelectorAll(
|
|
"#computed-container .computed-property-view"
|
|
)) {
|
|
const nameSpan = property.querySelector(".computed-property-name");
|
|
if (nameSpan.firstChild.textContent === name) {
|
|
expander = property.querySelector(".computed-expandable");
|
|
matchedRulesEl = property.querySelector(".matchedselectors");
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!expander.hasAttribute("open")) {
|
|
// Need to expand the property
|
|
const onExpand = view.inspector.once("computed-view-property-expanded");
|
|
expander.click();
|
|
await onExpand;
|
|
|
|
await waitFor(() => expander.hasAttribute("open"));
|
|
}
|
|
|
|
return matchedRulesEl;
|
|
};
|
|
|
|
/**
|
|
* Get the text value of the property corresponding to a given name in the
|
|
* computed-view
|
|
*
|
|
* @param {CssComputedView} view
|
|
* The instance of the computed view panel
|
|
* @param {String} name
|
|
* The name of the property to retrieve
|
|
* @return {String} The property value
|
|
*/
|
|
function getComputedViewPropertyValue(view, name) {
|
|
return getComputedViewProperty(view, name).valueSpan.textContent;
|
|
}
|
|
|
|
/**
|
|
* Expand a given property, given its index in the current property list of
|
|
* the computed view
|
|
*
|
|
* @param {CssComputedView} view
|
|
* The instance of the computed view panel
|
|
* @param {Number} index
|
|
* The index of the property to be expanded
|
|
* @return a promise that resolves when the property has been expanded, or
|
|
* rejects if the property was not found
|
|
*/
|
|
function expandComputedViewPropertyByIndex(view, index) {
|
|
info("Expanding property " + index + " in the computed view");
|
|
const expandos = view.styleDocument.querySelectorAll(".computed-expandable");
|
|
if (!expandos.length || !expandos[index]) {
|
|
return Promise.reject();
|
|
}
|
|
|
|
const onExpand = view.inspector.once("computed-view-property-expanded");
|
|
expandos[index].click();
|
|
return onExpand;
|
|
}
|
|
|
|
/**
|
|
* Get a rule-link from the computed-view given its index
|
|
*
|
|
* @param {CssComputedView} view
|
|
* The instance of the computed view panel
|
|
* @param {Number} index
|
|
* The index of the link to be retrieved
|
|
* @return {DOMNode} The link at the given index, if one exists, null otherwise
|
|
*/
|
|
function getComputedViewLinkByIndex(view, index) {
|
|
const links = view.styleDocument.querySelectorAll(
|
|
".rule-link .computed-link"
|
|
);
|
|
return links[index];
|
|
}
|
|
|
|
/**
|
|
* Trigger the select all action in the computed view.
|
|
*
|
|
* @param {CssComputedView} view
|
|
* The instance of the computed view panel
|
|
*/
|
|
function selectAllText(view) {
|
|
info("Selecting all the text");
|
|
view.contextMenu._onSelectAll();
|
|
}
|
|
|
|
/**
|
|
* Select all the text, copy it, and check the content in the clipboard.
|
|
*
|
|
* @param {CssComputedView} view
|
|
* The instance of the computed view panel
|
|
* @param {String} expectedPattern
|
|
* A regular expression used to check the content of the clipboard
|
|
*/
|
|
async function copyAllAndCheckClipboard(view, expectedPattern) {
|
|
selectAllText(view);
|
|
const contentDoc = view.styleDocument;
|
|
const prop = contentDoc.querySelector(
|
|
"#computed-container .computed-property-view"
|
|
);
|
|
|
|
try {
|
|
info("Trigger a copy event and wait for the clipboard content");
|
|
await waitForClipboardPromise(
|
|
() => fireCopyEvent(prop),
|
|
() => checkClipboard(expectedPattern)
|
|
);
|
|
} catch (e) {
|
|
failClipboardCheck(expectedPattern);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Select some text, copy it, and check the content in the clipboard.
|
|
*
|
|
* @param {CssComputedView} view
|
|
* The instance of the computed view panel
|
|
* @param {Object} positions
|
|
* The start and end positions of the text to be selected. This must be an object
|
|
* like this:
|
|
* { start: {prop: 1, offset: 0}, end: {prop: 3, offset: 5} }
|
|
* @param {String} expectedPattern
|
|
* A regular expression used to check the content of the clipboard
|
|
*/
|
|
async function copySomeTextAndCheckClipboard(view, positions, expectedPattern) {
|
|
info("Testing selection copy");
|
|
|
|
const contentDocument = view.styleDocument;
|
|
const props = contentDocument.querySelectorAll(
|
|
"#computed-container .computed-property-view"
|
|
);
|
|
|
|
info("Create the text selection range");
|
|
const range = contentDocument.createRange();
|
|
range.setStart(props[positions.start.prop], positions.start.offset);
|
|
range.setEnd(props[positions.end.prop], positions.end.offset);
|
|
contentDocument.defaultView.getSelection().addRange(range);
|
|
|
|
try {
|
|
info("Trigger a copy event and wait for the clipboard content");
|
|
await waitForClipboardPromise(
|
|
() => fireCopyEvent(props[0]),
|
|
() => checkClipboard(expectedPattern)
|
|
);
|
|
} catch (e) {
|
|
failClipboardCheck(expectedPattern);
|
|
}
|
|
}
|
|
|
|
function checkClipboard(expectedPattern) {
|
|
const actual = SpecialPowers.getClipboardData("text/plain");
|
|
const expectedRegExp = new RegExp(expectedPattern, "g");
|
|
return expectedRegExp.test(actual);
|
|
}
|
|
|
|
function failClipboardCheck(expectedPattern) {
|
|
// Format expected text for comparison
|
|
const terminator = Services.appinfo.OS == "WINNT" ? "\r\n" : "\n";
|
|
expectedPattern = expectedPattern.replace(/\[\\r\\n\][+*]/g, terminator);
|
|
expectedPattern = expectedPattern.replace(/\\\(/g, "(");
|
|
expectedPattern = expectedPattern.replace(/\\\)/g, ")");
|
|
|
|
let actual = SpecialPowers.getClipboardData("text/plain");
|
|
|
|
// Trim the right hand side of our strings. This is because expectedPattern
|
|
// accounts for windows sometimes adding a newline to our copied data.
|
|
expectedPattern = expectedPattern.trimRight();
|
|
actual = actual.trimRight();
|
|
|
|
dump(
|
|
"TEST-UNEXPECTED-FAIL | Clipboard text does not match expected ... " +
|
|
"results (escaped for accurate comparison):\n"
|
|
);
|
|
info("Actual: " + escape(actual));
|
|
info("Expected: " + escape(expectedPattern));
|
|
}
|