blob: ba5f6e09363ca1071bdab4331673cfd1c8df09f4 [file] [log] [blame]
// Copyright 2017 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.
/**
* @fileoverview using private properties isn't a Closure violation in tests.
* @suppress {accessControls}
*/
/**
* @param {string} idValue
* @param {!Function} callback
*/
ElementsTestRunner.selectNodeWithId = function(idValue, callback) {
callback = TestRunner.safeWrap(callback);
function onNodeFound(node) {
ElementsTestRunner.selectNode(node).then(callback.bind(null, node));
}
ElementsTestRunner.nodeWithId(idValue, onNodeFound);
};
/**
* @param {!Object} node
* @return {!Promise.<undefined>}
*/
ElementsTestRunner.selectNode = function(node) {
return Common.Revealer.reveal(node);
};
/**
* @param {string} idValue
* @param {!Function} callback
*/
ElementsTestRunner.nodeWithId = function(idValue, callback) {
ElementsTestRunner.findNode(node => node.getAttribute('id') === idValue, callback);
};
/**
* @param {string} idValue
* @param {!Function} callback
*/
ElementsTestRunner.nodeWithIdPromise = function(idValue) {
return new Promise(resolve => ElementsTestRunner.findNode(node => node.getAttribute('id') === idValue, resolve));
};
/**
* @param {function(!Element): boolean} matchFunction
* @param {!Function} callback
*/
ElementsTestRunner.findNode = async function(matchFunction, callback) {
callback = TestRunner.safeWrap(callback);
let result = null;
let pendingRequests = 0;
async function processChildren(node) {
try {
if (result) {
return;
}
if (node._childDocumentPromiseForTesting) {
await node._childDocumentPromiseForTesting;
}
const pseudoElementsMap = node.pseudoElements();
const pseudoElements = pseudoElementsMap ? pseudoElementsMap.valuesArray() : [];
const children = (node.children() || []).concat(node.shadowRoots()).concat(pseudoElements);
if (node.templateContent()) {
children.push(node.templateContent());
} else if (node.contentDocument()) {
children.push(node.contentDocument());
} else if (node.importedDocument()) {
children.push(node.importedDocument());
}
for (let i = 0; i < children.length; ++i) {
const childNode = children[i];
if (matchFunction(childNode)) {
result = childNode;
callback(result);
return;
}
pendingRequests++;
childNode.getChildNodes(processChildren.bind(null, childNode));
}
} finally {
pendingRequests--;
}
if (!result && !pendingRequests) {
callback(null);
}
}
const doc = TestRunner.domModel.existingDocument() || await TestRunner.domModel.requestDocument();
pendingRequests++;
doc.getChildNodes(processChildren.bind(null, doc));
};
/**
* @param {function(!Element): boolean} matchFunction
* @param {!Promise}
*/
ElementsTestRunner.findNodePromise = function(matchFunction) {
return new Promise(resolve => ElementsTestRunner.findNode(matchFunction, resolve));
};
/**
* @param {!EventListeners.EventListenersView} eventListenersView
* @param {function():void} callback
* @param {boolean=} force
*/
ElementsTestRunner.expandAndDumpEventListeners = function(eventListenersView, callback, force) {
function listenersArrived() {
const listenerTypes = eventListenersView._treeOutline.rootElement().children();
for (let i = 0; i < listenerTypes.length; ++i) {
listenerTypes[i].expand();
const listenerItems = listenerTypes[i].children();
for (let j = 0; j < listenerItems.length; ++j) {
listenerItems[j].expand();
}
}
TestRunner.deprecatedRunAfterPendingDispatches(objectsExpanded);
}
function objectsExpanded() {
const listenerTypes = eventListenersView._treeOutline.rootElement().children();
for (let i = 0; i < listenerTypes.length; ++i) {
if (!listenerTypes[i].children().length) {
continue;
}
const eventType = listenerTypes[i]._title;
TestRunner.addResult('');
TestRunner.addResult('======== ' + eventType + ' ========');
const listenerItems = listenerTypes[i].children();
for (let j = 0; j < listenerItems.length; ++j) {
TestRunner.addResult('== ' + listenerItems[j].eventListener().origin());
TestRunner.dumpObjectPropertyTreeElement(listenerItems[j]);
}
}
callback();
}
if (force) {
listenersArrived();
} else {
TestRunner.addSniffer(
EventListeners.EventListenersView.prototype, '_eventListenersArrivedForTest', listenersArrived);
}
};
/**
* @param {!EventListeners.EventListenersView} eventListenersView
* @param {boolean=} force
* @return {!Promise}
*/
ElementsTestRunner.expandAndDumpEventListenersPromise = function(eventListenersView, force) {
return new Promise(resolve => ElementsTestRunner.expandAndDumpEventListeners(eventListenersView, resolve, force));
};
ElementsTestRunner.inlineStyleSection = function() {
return UI.panels.elements._stylesWidget._sectionBlocks[0].sections[0];
};
ElementsTestRunner.computedStyleWidget = function() {
return UI.panels.elements._computedStyleWidget;
};
ElementsTestRunner.dumpComputedStyle = function(doNotAutoExpand, printInnerText) {
const computed = ElementsTestRunner.computedStyleWidget();
const treeOutline = computed._propertiesOutline;
const children = treeOutline.rootElement().children();
for (const treeElement of children) {
const property = treeElement[Elements.ComputedStyleWidget._propertySymbol];
if (property.name === 'width' || property.name === 'height') {
continue;
}
let dumpText = '';
dumpText += text(treeElement.title.querySelector('.property-name'));
dumpText += text(treeElement.title.querySelector('.property-value'));
TestRunner.addResult(dumpText);
if (doNotAutoExpand && !treeElement.expanded) {
continue;
}
for (const trace of treeElement.children()) {
const title = trace.title;
let dumpText = '';
if (trace.title.classList.contains('property-trace-inactive')) {
dumpText += 'OVERLOADED ';
}
dumpText += text(title.querySelector('.property-trace-value'));
dumpText += ' - ';
dumpText += text(title.querySelector('.property-trace-selector'));
const link = title.querySelector('.trace-link');
if (link) {
dumpText += ' ' + extractLinkText(link);
}
TestRunner.addResult(' ' + dumpText);
}
}
function text(node) {
return printInnerText ? node.innerText : node.textContent;
}
};
ElementsTestRunner.findComputedPropertyWithName = function(name) {
const computed = ElementsTestRunner.computedStyleWidget();
const treeOutline = computed._propertiesOutline;
const children = treeOutline.rootElement().children();
for (const treeElement of children) {
const property = treeElement[Elements.ComputedStyleWidget._propertySymbol];
if (property.name === name) {
return treeElement;
}
}
return null;
};
ElementsTestRunner.firstMatchedStyleSection = function() {
return UI.panels.elements._stylesWidget._sectionBlocks[0].sections[1];
};
ElementsTestRunner.firstMediaTextElementInSection = function(section) {
return section.element.querySelector('.media-text');
};
ElementsTestRunner.querySelector = async function(selector, callback) {
const doc = await TestRunner.domModel.requestDocument();
const nodeId = await TestRunner.domModel.querySelector(doc.id, selector);
callback(TestRunner.domModel.nodeForId(nodeId));
};
ElementsTestRunner.shadowRootByHostId = function(idValue, callback) {
function shadowRootMatches(node) {
return node.isShadowRoot() && node.parentNode.getAttribute('id') === idValue;
}
ElementsTestRunner.findNode(shadowRootMatches, callback);
};
ElementsTestRunner.nodeWithClass = function(classValue, callback) {
function nodeClassMatches(node) {
const classAttr = node.getAttribute('class');
return classAttr && classAttr.indexOf(classValue) > -1;
}
ElementsTestRunner.findNode(nodeClassMatches, callback);
};
ElementsTestRunner.expandedNodeWithId = function(idValue) {
let result;
ElementsTestRunner.nodeWithId(idValue, node => result = node);
return result;
};
function waitForStylesRebuild(matchFunction, callback, requireRebuild) {
(function sniff(node, rebuild) {
if ((rebuild || !requireRebuild) && node && matchFunction(node)) {
callback();
return;
}
TestRunner.addSniffer(Elements.StylesSidebarPane.prototype, '_nodeStylesUpdatedForTest', sniff);
})(null);
}
ElementsTestRunner.waitForStyles = function(idValue, callback, requireRebuild) {
callback = TestRunner.safeWrap(callback);
function nodeWithId(node) {
return node.getAttribute('id') === idValue;
}
waitForStylesRebuild(nodeWithId, callback, requireRebuild);
};
ElementsTestRunner.waitForStyleCommitted = function(next) {
TestRunner.addSniffer(Elements.StylePropertyTreeElement.prototype, '_editingCommitted', (...args) => {
Promise.all(args).then(next);
});
};
ElementsTestRunner.waitForStylesForClass = function(classValue, callback, requireRebuild) {
callback = TestRunner.safeWrap(callback);
function nodeWithClass(node) {
const classAttr = node.getAttribute('class');
return classAttr && classAttr.indexOf(classValue) > -1;
}
waitForStylesRebuild(nodeWithClass, callback, requireRebuild);
};
ElementsTestRunner.waitForSelectorCommitted = function(callback) {
TestRunner.addSniffer(Elements.StylePropertiesSection.prototype, '_editingSelectorCommittedForTest', callback);
};
ElementsTestRunner.waitForMediaTextCommitted = function(callback) {
TestRunner.addSniffer(Elements.StylePropertiesSection.prototype, '_editingMediaTextCommittedForTest', callback);
};
ElementsTestRunner.waitForStyleApplied = function(callback) {
TestRunner.addSniffer(Elements.StylePropertyTreeElement.prototype, 'styleTextAppliedForTest', callback);
};
ElementsTestRunner.waitForStyleAppliedPromise = function() {
return new Promise(resolve => ElementsTestRunner.waitForStyleApplied(resolve));
};
ElementsTestRunner.selectNodeAndWaitForStyles = function(idValue, callback) {
callback = TestRunner.safeWrap(callback);
let targetNode;
ElementsTestRunner.waitForStyles(idValue, stylesUpdated, true);
ElementsTestRunner.selectNodeWithId(idValue, nodeSelected);
function nodeSelected(node) {
targetNode = node;
}
function stylesUpdated() {
callback(targetNode);
}
};
ElementsTestRunner.selectNodeAndWaitForStylesPromise = function(idValue) {
return new Promise(x => ElementsTestRunner.selectNodeAndWaitForStyles(idValue, x));
};
ElementsTestRunner.selectPseudoElementAndWaitForStyles = function(parentId, pseudoType, callback) {
callback = TestRunner.safeWrap(callback);
let targetNode;
waitForStylesRebuild(isPseudoElement, stylesUpdated, true);
ElementsTestRunner.findNode(isPseudoElement, nodeFound);
function nodeFound(node) {
targetNode = node;
Common.Revealer.reveal(node);
}
function stylesUpdated() {
callback(targetNode);
}
function isPseudoElement(node) {
return node.parentNode && node.parentNode.getAttribute('id') === parentId && node.pseudoType() === pseudoType;
}
};
ElementsTestRunner.selectNodeAndWaitForStylesWithComputed = function(idValue, callback) {
callback = TestRunner.safeWrap(callback);
ElementsTestRunner.selectNodeAndWaitForStyles(idValue, onSidebarRendered);
function onSidebarRendered(node) {
ElementsTestRunner.computedStyleWidget().doUpdate().then(callback.bind(null, node));
}
};
ElementsTestRunner.firstElementsTreeOutline = function() {
return UI.panels.elements._treeOutlines[0];
};
ElementsTestRunner.filterMatchedStyles = function(text) {
const regex = (text ? new RegExp(text, 'i') : null);
TestRunner.addResult('Filtering styles by: ' + text);
UI.panels.elements._stylesWidget._onFilterChanged(regex);
};
ElementsTestRunner.dumpRenderedMatchedStyles = function() {
const sectionBlocks = UI.panels.elements._stylesWidget._sectionBlocks;
for (const block of sectionBlocks) {
for (const section of block.sections) {
if (section.element.classList.contains('hidden')) {
continue;
}
dumpRenderedSection(section);
}
}
function dumpRenderedSection(section) {
TestRunner.addResult(section._selectorElement.textContent + ' {');
const rootElement = section.propertiesTreeOutline.rootElement();
for (let i = 0; i < rootElement.childCount(); ++i) {
dumpRenderedProperty(rootElement.childAt(i));
}
TestRunner.addResult('}');
}
function dumpRenderedProperty(property) {
let text = new Array(4).join(' ');
text += property.nameElement.textContent;
text += ':';
if (property.isExpandable()) {
text += (property.expanded ? 'v' : '>');
} else {
text += ' ';
}
text += property.valueElement.textContent;
if (property.listItemElement.classList.contains('filter-match')) {
text = 'F' + text.substring(1);
}
TestRunner.addResult(text);
if (!property.expanded) {
return;
}
const indent = new Array(8).join(' ');
for (let i = 0; i < property.childCount(); ++i) {
const childProperty = property.childAt(i);
let text = indent;
text += String.sprintf('%s: %s', childProperty.nameElement.textContent, childProperty.valueElement.textContent);
if (childProperty.listItemElement.classList.contains('filter-match')) {
text = 'F' + text.substring(1);
}
TestRunner.addResult(text);
}
}
};
ElementsTestRunner.dumpSelectedElementStyles = function(
excludeComputed, excludeMatched, omitLonghands, includeSelectorGroupMarks, printInnerText) {
const sectionBlocks = UI.panels.elements._stylesWidget._sectionBlocks;
if (!excludeComputed) {
ElementsTestRunner.dumpComputedStyle(false /* doNotAutoExpand */, printInnerText);
}
for (const block of sectionBlocks) {
for (const section of block.sections) {
if (section.style().parentRule && excludeMatched) {
continue;
}
if (section.element.previousSibling && section.element.previousSibling.className === 'sidebar-separator') {
let nodeDescription = '';
if (section.element.previousSibling.firstElementChild) {
nodeDescription = text(section.element.previousSibling.firstElementChild.shadowRoot.lastChild);
}
TestRunner.addResult('======== ' + text(section.element.previousSibling) + nodeDescription + ' ========');
}
printStyleSection(section, omitLonghands, includeSelectorGroupMarks, printInnerText);
}
}
function text(node) {
return printInnerText ? node.innerText : node.textContent;
}
};
function printStyleSection(section, omitLonghands, includeSelectorGroupMarks, printInnerText) {
if (!section) {
return;
}
TestRunner.addResult(
'[expanded] ' + ((section.propertiesTreeOutline.element.classList.contains('no-affect') ? '[no-affect] ' : '')));
const medias = section._titleElement.querySelectorAll('.media-list .media');
for (let i = 0; i < medias.length; ++i) {
const media = medias[i];
TestRunner.addResult(text(media));
}
const selector =
section._titleElement.querySelector('.selector') || section._titleElement.querySelector('.keyframe-key');
let selectorText = (includeSelectorGroupMarks ? buildMarkedSelectors(selector) : text(selector));
selectorText += text(selector.nextSibling);
const anchor = section._titleElement.querySelector('.styles-section-subtitle');
if (anchor) {
const anchorText = extractLinkText(anchor);
selectorText += String.sprintf(' (%s)', anchorText);
}
TestRunner.addResult(selectorText);
ElementsTestRunner.dumpStyleTreeOutline(section.propertiesTreeOutline, (omitLonghands ? 1 : 2), printInnerText);
if (!section._showAllButton.classList.contains('hidden')) {
TestRunner.addResult(text(section._showAllButton));
}
TestRunner.addResult('');
function text(node) {
return printInnerText ? node.innerText : node.textContent;
}
}
function extractLinkText(element) {
const anchor = element.querySelector('.devtools-link');
if (!anchor) {
return element.textContent;
}
const anchorText = anchor.textContent;
const info = Components.Linkifier._linkInfo(anchor);
const uiLocation = info && info.uiLocation;
const anchorTarget =
(uiLocation ?
uiLocation.uiSourceCode.name() + ':' + (uiLocation.lineNumber + 1) + ':' + (uiLocation.columnNumber + 1) :
'');
return anchorText + ' -> ' + anchorTarget;
}
function buildMarkedSelectors(element) {
let result = '';
for (let node = element.firstChild; node; node = node.nextSibling) {
if (node.nodeType === Node.ELEMENT_NODE && node.classList.contains('selector-matches')) {
result += '[$' + node.textContent + '$]';
} else {
result += node.textContent;
}
}
return result;
}
ElementsTestRunner.toggleStyleProperty = function(propertyName, checked) {
const treeItem = ElementsTestRunner.getElementStylePropertyTreeItem(propertyName);
treeItem._toggleDisabled(!checked);
};
ElementsTestRunner.toggleMatchedStyleProperty = function(propertyName, checked) {
const treeItem = ElementsTestRunner.getMatchedStylePropertyTreeItem(propertyName);
treeItem._toggleDisabled(!checked);
};
ElementsTestRunner.eventListenersWidget = function() {
UI.viewManager.showView('elements.eventListeners');
return self.runtime.sharedInstance(Elements.EventListenersWidget);
};
ElementsTestRunner.showEventListenersWidget = function() {
return UI.viewManager.showView('elements.eventListeners');
};
/**
* @return {Promise}
*/
ElementsTestRunner.showComputedStyles = function() {
UI.panels.elements.sidebarPaneView.tabbedPane().selectTab('Computed', true);
return ElementsTestRunner.computedStyleWidget().doUpdate();
};
ElementsTestRunner.expandAndDumpSelectedElementEventListeners = function(callback, force) {
ElementsTestRunner.expandAndDumpEventListeners(
ElementsTestRunner.eventListenersWidget()._eventListenersView, callback, force);
};
ElementsTestRunner.removeFirstEventListener = function() {
const treeOutline = ElementsTestRunner.eventListenersWidget()._eventListenersView._treeOutline;
const listenerTypes = treeOutline.rootElement().children();
for (let i = 0; i < listenerTypes.length; i++) {
const listeners = listenerTypes[i].children();
if (listeners.length && !listenerTypes[i].hidden) {
listeners[0].eventListener().remove();
listeners[0]._removeListenerBar();
break;
}
}
};
ElementsTestRunner.dumpObjectPropertySectionDeep = function(section) {
function domNodeToString(node) {
if (node) {
return '\'' + node.textContent + '\'';
} else {
return 'null';
}
}
function dumpTreeElementRecursively(treeElement, prefix) {
if ('nameElement' in treeElement) {
TestRunner.addResult(
prefix + domNodeToString(treeElement.nameElement) + ' => ' + domNodeToString(treeElement.valueElement));
} else {
TestRunner.addResult(prefix + treeElement.title);
}
for (let i = 0; i < treeElement.childCount(); i++) {
dumpTreeElementRecursively(treeElement.childAt(i), prefix + ' ');
}
}
const childNodes = section.propertiesTreeOutline.rootElement().children();
for (let i = 0; i < childNodes.length; i++) {
dumpTreeElementRecursively(childNodes[i], '');
}
};
ElementsTestRunner.getElementStylePropertyTreeItem = function(propertyName) {
return ElementsTestRunner.getFirstPropertyTreeItemForSection(ElementsTestRunner.inlineStyleSection(), propertyName);
};
ElementsTestRunner.getMatchedStylePropertyTreeItem = function(propertyName) {
const sectionBlocks = UI.panels.elements._stylesWidget._sectionBlocks;
for (const block of sectionBlocks) {
for (const section of block.sections) {
const treeItem = ElementsTestRunner.getFirstPropertyTreeItemForSection(section, propertyName);
if (treeItem) {
return treeItem;
}
}
}
return null;
};
ElementsTestRunner.getFirstPropertyTreeItemForSection = function(section, propertyName) {
const outline = section.propertiesTreeOutline.rootElement();
for (let i = 0; i < outline.childCount(); ++i) {
const treeItem = outline.childAt(i);
if (treeItem.name === propertyName) {
return treeItem;
}
}
return null;
};
ElementsTestRunner.dumpStyleTreeOutline = function(treeItem, depth, printInnerText) {
const children = treeItem.rootElement().children();
for (let i = 0; i < children.length; ++i) {
ElementsTestRunner.dumpStyleTreeItem(children[i], '', depth || 2, printInnerText);
}
};
ElementsTestRunner.dumpStyleTreeItem = function(treeItem, prefix, depth, printInnerText) {
const textContent = printInnerText ? treeItem.listItemElement.innerText :
TestRunner.textContentWithoutStyles(treeItem.listItemElement);
if (textContent.indexOf(' width:') !== -1 || textContent.indexOf(' height:') !== -1) {
return;
}
if (treeItem.listItemElement.classList.contains('inherited')) {
return;
}
let typePrefix = '';
if (treeItem.listItemElement.classList.contains('overloaded') ||
treeItem.listItemElement.classList.contains('inactive') ||
treeItem.listItemElement.classList.contains('not-parsed-ok')) {
typePrefix += '/-- overloaded --/ ';
}
if (treeItem.listItemElement.classList.contains('disabled')) {
typePrefix += '/-- disabled --/ ';
}
TestRunner.addResult(prefix + typePrefix + textContent);
if (--depth) {
treeItem.expand();
const children = treeItem.children();
for (let i = 0; children && i < children.length; ++i) {
ElementsTestRunner.dumpStyleTreeItem(children[i], prefix + ' ', depth);
}
}
};
ElementsTestRunner.dumpElementsTree = function(rootNode, depth, resultsArray) {
function beautify(element) {
return element.innerText.replace(/\u200b/g, '').replace(/\n/g, '\\n').trim();
}
function dumpMap(name, map) {
const result = [];
for (const id of map.keys()) {
result.push(id + '=' + map.get(id));
}
if (!result.length) {
return '';
}
return name + ':[' + result.join(',') + ']';
}
function markersDataDump(treeItem) {
if (treeItem._elementCloseTag) {
return '';
}
let markers = '';
const node = treeItem._node;
if (node) {
markers += dumpMap('markers', node._markers);
const dump = (node._subtreeMarkerCount ? 'subtreeMarkerCount:' + node._subtreeMarkerCount : '');
if (dump) {
if (markers) {
markers += ', ';
}
markers += dump;
}
if (markers) {
markers = ' [' + markers + ']';
}
}
return markers;
}
function print(treeItem, prefix, depth) {
if (!treeItem.root) {
let expander;
if (treeItem.isExpandable()) {
if (treeItem.expanded) {
expander = '- ';
} else {
expander = '+ ';
}
} else {
expander = ' ';
}
const markers = markersDataDump(treeItem);
let value = prefix + expander + beautify(treeItem.listItemElement) + markers;
if (treeItem.shadowHostToolbar) {
value = prefix + expander + 'shadow-root ';
for (let i = 0; i < treeItem.shadowHostToolbar.children.length; ++i) {
const button = treeItem.shadowHostToolbar.children[i];
const toggled = button.disabled;
const name = ((toggled ? '<' : '')) + button.textContent + ((toggled ? '>' : ''));
value += name + ' ';
}
}
if (resultsArray) {
resultsArray.push(value);
} else {
TestRunner.addResult(value);
}
}
if (!treeItem.expanded) {
return;
}
const children = treeItem.children();
const newPrefix = (treeItem.root ? '' : prefix + ' ');
for (let i = 0; depth && children && i < children.length; ++i) {
if (!children[i]._elementCloseTag) {
print(children[i], newPrefix, depth - 1);
} else {
print(children[i], prefix, depth);
}
}
}
const treeOutline = ElementsTestRunner.firstElementsTreeOutline();
treeOutline.runPendingUpdates();
print((rootNode ? treeOutline.findTreeElement(rootNode) : treeOutline.rootElement()), '', depth || 10000);
};
ElementsTestRunner.dumpDOMUpdateHighlights = function(rootNode, callback, depth) {
let hasHighlights = false;
TestRunner.addSniffer(Elements.ElementsTreeOutline.prototype, '_updateModifiedNodes', didUpdate);
function didUpdate() {
const treeOutline = ElementsTestRunner.firstElementsTreeOutline();
print((rootNode ? treeOutline.findTreeElement(rootNode) : treeOutline.rootElement()), '', depth || 10000);
if (!hasHighlights) {
TestRunner.addResult('<No highlights>');
}
if (callback) {
callback();
}
}
function print(treeItem, prefix, depth) {
if (!treeItem.root) {
const elementXPath = Elements.DOMPath.xPath(treeItem.node(), true);
const highlightedElements = treeItem.listItemElement.querySelectorAll('.dom-update-highlight');
for (let i = 0; i < highlightedElements.length; ++i) {
const element = highlightedElements[i];
const classList = element.classList;
let xpath = elementXPath;
if (classList.contains('webkit-html-attribute-name')) {
xpath += '/@' + element.textContent + ' (empty)';
} else if (classList.contains('webkit-html-attribute-value')) {
name = element.parentElement.querySelector('.webkit-html-attribute-name').textContent;
xpath += '/@' + name + ' ' + element.textContent;
} else if (classList.contains('webkit-html-text-node')) {
xpath += '/text() "' + element.textContent + '"';
}
TestRunner.addResult(prefix + xpath);
hasHighlights = true;
}
}
if (!treeItem.expanded) {
return;
}
const children = treeItem.children();
const newPrefix = (treeItem.root ? '' : prefix + ' ');
for (let i = 0; depth && children && i < children.length; ++i) {
if (!children[i]._elementCloseTag) {
print(children[i], newPrefix, depth - 1);
}
}
}
};
ElementsTestRunner.expandElementsTree = function(callback) {
let expandedSomething = false;
callback = TestRunner.safeWrap(callback);
function expand(treeItem) {
const children = treeItem.children();
for (let i = 0; children && i < children.length; ++i) {
const child = children[i];
if (child.isExpandable() && !child.expanded) {
child.expand();
expandedSomething = true;
}
expand(child);
}
}
function onAllNodesAvailable() {
ElementsTestRunner.firstElementsTreeOutline().runPendingUpdates();
expand(ElementsTestRunner.firstElementsTreeOutline().rootElement());
setTimeout(callback.bind(null, expandedSomething));
}
ElementsTestRunner.findNode(function() {
return false;
}, onAllNodesAvailable);
};
ElementsTestRunner.expandAndDump = function() {
TestRunner.addResult('\nDump tree');
let callback;
const result = new Promise(f => callback = f);
ElementsTestRunner.expandElementsTree(() => {
ElementsTestRunner.dumpElementsTree();
callback();
});
return result;
};
ElementsTestRunner.dumpDOMAgentTree = function(node) {
if (!TestRunner.domModel._document) {
return;
}
function dump(node, prefix) {
TestRunner.addResult(prefix + node.nodeName());
prefix = prefix + ' ';
if (node.templateContent()) {
dump(node.templateContent(), prefix);
}
if (node.contentDocument()) {
dump(node.contentDocument(), prefix);
}
if (node.importedDocument()) {
dump(node.importedDocument(), prefix);
}
const shadowRoots = node.shadowRoots();
for (let i = 0; i < shadowRoots.length; ++i) {
dump(shadowRoots[i], prefix);
}
const children = node.children();
for (let i = 0; children && i < children.length; ++i) {
dump(children[i], prefix);
}
}
dump(node, '');
};
ElementsTestRunner.rangeText = function(range) {
if (!range) {
return '[undefined-undefined]';
}
return '[' + range.startLine + ':' + range.startColumn + '-' + range.endLine + ':' + range.endColumn + ']';
};
ElementsTestRunner.generateUndoTest = function(testBody) {
function result(next) {
const testNode = ElementsTestRunner.expandedNodeWithId(/function\s([^(]*)/.exec(testBody)[1]);
TestRunner.addResult('Initial:');
ElementsTestRunner.dumpElementsTree(testNode);
testBody(undo);
function undo() {
TestRunner.addResult('Post-action:');
ElementsTestRunner.dumpElementsTree(testNode);
ElementsTestRunner.expandElementsTree(expandedCallback);
function expandedCallback(expandedSomething) {
if (expandedSomething) {
TestRunner.addResult('== Expanded: ==');
ElementsTestRunner.dumpElementsTree(testNode);
}
SDK.domModelUndoStack.undo().then(redo);
}
}
function redo() {
TestRunner.addResult('Post-undo (initial):');
ElementsTestRunner.dumpElementsTree(testNode);
ElementsTestRunner.expandElementsTree(expandedCallback);
function expandedCallback(expandedSomething) {
if (expandedSomething) {
TestRunner.addResult('== Expanded: ==');
ElementsTestRunner.dumpElementsTree(testNode);
}
SDK.domModelUndoStack.redo().then(done);
}
}
function done() {
TestRunner.addResult('Post-redo (action):');
ElementsTestRunner.dumpElementsTree(testNode);
ElementsTestRunner.expandElementsTree(expandedCallback);
function expandedCallback(expandedSomething) {
if (expandedSomething) {
TestRunner.addResult('== Expanded: ==');
ElementsTestRunner.dumpElementsTree(testNode);
}
next();
}
}
}
result.toString = function() {
return testBody.toString();
};
return result;
};
const indent = ' ';
ElementsTestRunner.dumpRulesArray = function(rules, currentIndent) {
if (!rules) {
return;
}
currentIndent = currentIndent || '';
for (let i = 0; i < rules.length; ++i) {
ElementsTestRunner.dumpRule(rules[i], currentIndent);
}
};
ElementsTestRunner.dumpRuleMatchesArray = function(matches, currentIndent) {
if (!matches) {
return;
}
currentIndent = currentIndent || '';
for (let i = 0; i < matches.length; ++i) {
ElementsTestRunner.dumpRule(matches[i].rule, currentIndent);
}
};
ElementsTestRunner.dumpRule = function(rule, currentIndent) {
function selectorRange() {
const selectors = rule.selectorList.selectors;
if (!selectors || !selectors[0].range) {
return '';
}
const ranges = [];
for (let i = 0; i < selectors.length; ++i) {
const range = selectors[i].range;
ranges.push(range.startLine + ':' + range.startColumn + '-' + range.endLine + ':' + range.endColumn);
}
return ', ' + ranges.join('; ');
}
currentIndent = currentIndent || '';
if (!rule.type || rule.type === 'style') {
TestRunner.addResult(currentIndent + rule.selectorList.text + ': [' + rule.origin + selectorRange() + '] {');
ElementsTestRunner.dumpStyle(rule.style, currentIndent + indent);
TestRunner.addResult(currentIndent + '}');
return;
}
if (rule.type === 'media') {
TestRunner.addResult(currentIndent + '@media ' + rule.mediaText + ' {');
ElementsTestRunner.dumpRulesArray(rule.childRules, currentIndent + indent);
TestRunner.addResult(currentIndent + '}');
return;
}
if (rule.type === 'import') {
TestRunner.addResult(
currentIndent + '@import: header=' + ElementsTestRunner.rangeText(rule.headerRange) +
', body=' + ElementsTestRunner.rangeText(rule.bodyRange));
return;
}
if (rule.type === 'page' || rule.type === 'font-face') {
if (rule.type === 'page') {
TestRunner.addResult(currentIndent + rule.selectorList.text + ' {');
} else {
TestRunner.addResult(
currentIndent + '@' + rule.type + ' ' + ((rule.selectorList.text ? rule.selectorList.text + ' ' : '')) + '{');
}
ElementsTestRunner.dumpStyle(rule.style, currentIndent + indent);
TestRunner.addResult(currentIndent + '}');
return;
}
if (rule.type === 'charset') {
TestRunner.addResult('@charset');
return;
}
TestRunner.addResult(
currentIndent + '[UNKNOWN RULE]: header=' + ElementsTestRunner.rangeText(rule.headerRange) +
', body=' + ElementsTestRunner.rangeText(rule.bodyRange));
};
ElementsTestRunner.dumpStyle = function(style, currentIndent) {
currentIndent = currentIndent || '';
if (!style) {
TestRunner.addResult(currentIndent + '[NO STYLE]');
return;
}
for (let i = 0; i < style.cssProperties.length; ++i) {
const property = style.cssProperties[i];
if (!property.disabled) {
TestRunner.addResult(
currentIndent + '[\'' + property.name + '\':\'' + property.value + '\'' +
((property.important ? ' is-important' : '')) + (('parsedOk' in property ? ' non-parsed' : '')) + '] @' +
ElementsTestRunner.rangeText(property.range) + ' ');
} else {
TestRunner.addResult(currentIndent + '[text=\'' + property.text + '\'] disabled');
}
}
};
ElementsTestRunner.dumpCSSStyleDeclaration = function(style, currentIndent) {
currentIndent = currentIndent || '';
if (!style) {
TestRunner.addResult(currentIndent + '[NO STYLE]');
return;
}
const properties = style.allProperties();
for (let i = 0; i < properties.length; ++i) {
const property = properties[i];
if (!property.disabled) {
TestRunner.addResult(
currentIndent + '[\'' + property.name + '\':\'' + property.value + '\'' +
((property.important ? ' is-important' : '')) + ((!property['parsedOk'] ? ' non-parsed' : '')) + '] @' +
ElementsTestRunner.rangeText(property.range) + ' ');
} else {
TestRunner.addResult(currentIndent + '[text=\'' + property.text + '\'] disabled');
}
}
};
ElementsTestRunner.dumpBreadcrumb = function(message) {
if (message) {
TestRunner.addResult(message + ':');
}
const result = [];
const crumbs = UI.panels.elements._breadcrumbs.crumbsElement;
let crumb = crumbs.lastChild;
while (crumb) {
result.unshift(crumb.textContent);
crumb = crumb.previousSibling;
}
TestRunner.addResult(result.join(' > '));
};
ElementsTestRunner.matchingSelectors = function(matchedStyles, rule) {
const selectors = [];
const matchingSelectors = matchedStyles.matchingSelectors(rule);
for (let i = 0; i < matchingSelectors.length; ++i) {
selectors.push(rule.selectors[matchingSelectors[i]].text);
}
return '[' + selectors.join(', ') + ']';
};
ElementsTestRunner.addNewRuleInStyleSheet = function(styleSheetHeader, selector, callback) {
TestRunner.addSniffer(
Elements.StylesSidebarPane.prototype, '_addBlankSection', onBlankSection.bind(null, selector, callback));
UI.panels.elements._stylesWidget._createNewRuleInStyleSheet(styleSheetHeader);
};
ElementsTestRunner.addNewRule = function(selector, callback) {
UI.panels.elements._stylesWidget.contentElement.querySelector('.styles-pane-toolbar')
.shadowRoot.querySelector('.largeicon-add')
.click();
TestRunner.addSniffer(
Elements.StylesSidebarPane.prototype, '_addBlankSection', onBlankSection.bind(null, selector, callback));
};
function onBlankSection(selector, callback) {
const section = ElementsTestRunner.firstMatchedStyleSection();
if (typeof selector === 'string') {
section._selectorElement.textContent = selector;
}
section._selectorElement.dispatchEvent(TestRunner.createKeyEvent('Enter'));
ElementsTestRunner.waitForSelectorCommitted(callback.bind(null, section));
}
ElementsTestRunner.dumpInspectorHighlightJSON = function(idValue, callback) {
ElementsTestRunner.nodeWithId(idValue, nodeResolved);
async function nodeResolved(node) {
const result = await TestRunner.OverlayAgent.getHighlightObjectForTest(node.id);
TestRunner.addResult(idValue + JSON.stringify(result, null, 2));
callback();
}
};
ElementsTestRunner.dumpInspectorDistanceJSON = function(idValue, callback) {
ElementsTestRunner.nodeWithId(idValue, nodeResolved);
async function nodeResolved(node) {
const result = await TestRunner.OverlayAgent.getHighlightObjectForTest(node.id, true);
const info = result['distanceInfo'];
if (!info) {
TestRunner.addResult(`${idValue}: No distance info`);
} else {
if (info['style']) {
info['style'] = '<style data>';
}
TestRunner.addResult(idValue + JSON.stringify(info, null, 2));
}
callback();
}
};
ElementsTestRunner.dumpInspectorHighlightStyleJSON = async function(idValue) {
const node = await ElementsTestRunner.nodeWithIdPromise(idValue);
const result = await TestRunner.OverlayAgent.getHighlightObjectForTest(node.id, false, true /* includeStyle */);
const info = result['elementInfo'] ? result['elementInfo']['style'] : null;
if (!info) {
TestRunner.addResult(`${idValue}: No style info`);
} else {
if (info['font-family']) {
info['font-family'] = '<font-family value>';
}
TestRunner.addResult(idValue + JSON.stringify(info, null, 2));
}
};
ElementsTestRunner.waitForAnimationAdded = function(callback) {
TestRunner.addSniffer(Animation.AnimationTimeline.prototype, '_addAnimationGroup', callback);
};
ElementsTestRunner.dumpAnimationTimeline = function(timeline) {
for (const ui of timeline._uiAnimations) {
TestRunner.addResult(ui.animation().type());
TestRunner.addResult(ui._nameElement.innerHTML);
TestRunner.addResult(ui._svg.innerHTML);
}
};