blob: 54800d471f4d22ba8fafd586a48a8bbd6c5e3fdf [file] [log] [blame]
function createShadowRoot()
{
var children = Array.prototype.slice.call(arguments);
if ((children[0] instanceof Object) && !(children[0] instanceof Node))
return attachShadow.apply(null, children);
return {'isShadowRoot': true,
'children': children};
}
// TODO(kochi): This is not pure attachShadow wrapper, but also handles createShadowRoot()
// with attributes.
function attachShadow()
{
var children = Array.prototype.slice.call(arguments);
var attributes = {};
var parameter = {};
for (var key in children[0]) {
if (key == 'mode' || key == 'delegatesFocus')
parameter[key] = children[0][key];
else
attributes[key] = children[0][key];
}
return {'isShadowRoot': true,
'parameter': parameter,
'attributes': attributes,
'children': children.slice(1)};
}
function createUserAgentShadowRoot()
{
var shadowRoot = createShadowRoot.apply(null, arguments);
shadowRoot.isUserAgentShadowRoot = true;
return shadowRoot;
}
// This function can take optional child elements, which might be a result of createShadowRoot(), as arguments[2:].
function createDOM(tagName, attributes)
{
var element = document.createElement(tagName);
for (var name in attributes)
element.setAttribute(name, attributes[name]);
var childElements = Array.prototype.slice.call(arguments, 2);
for (var i = 0; i < childElements.length; ++i) {
var child = childElements[i];
if (child.isShadowRoot) {
var shadowRoot;
if (child.isUserAgentShadowRoot) {
shadowRoot = window.internals.createUserAgentShadowRoot(element);
} else {
if (child.parameter && Object.keys(child.parameter).length > 0)
shadowRoot = element.attachShadow(child.parameter);
else
shadowRoot = element.createShadowRoot();
}
if (child.attributes) {
for (var attribute in child.attributes) {
// Shadow Root does not have setAttribute.
shadowRoot[attribute] = child.attributes[attribute];
}
}
for (var j = 0; j < child.children.length; ++j)
shadowRoot.appendChild(child.children[j]);
} else
element.appendChild(child);
}
return element;
}
function removeWhiteSpaceOnlyTextNodes(node)
{
for (var i = 0; i < node.childNodes.length; i++) {
var child = node.childNodes[i];
if (child.nodeType === Node.TEXT_NODE && child.nodeValue.trim().length == 0) {
node.removeChild(child);
i--;
} else if (child.nodeType === Node.ELEMENT_NODE || child.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
removeWhiteSpaceOnlyTextNodes(child);
}
}
if (node.shadowRoot) {
removeWhiteSpaceOnlyTextNodes(node.shadowRoot);
}
}
function convertTemplatesToShadowRootsWithin(node) {
var nodes = node.querySelectorAll("template");
for (var i = 0; i < nodes.length; ++i) {
var template = nodes[i];
var mode = template.getAttribute("data-mode");
var parent = template.parentNode;
parent.removeChild(template);
var shadowRoot;
if (!mode || mode == 'v0'){
shadowRoot = parent.createShadowRoot();
} else {
shadowRoot = parent.attachShadow({'mode': mode});
}
var expose = template.getAttribute("data-expose-as");
if (expose)
window[expose] = shadowRoot;
if (template.id)
shadowRoot.id = template.id;
var fragments = document.importNode(template.content, true);
shadowRoot.appendChild(fragments);
convertTemplatesToShadowRootsWithin(shadowRoot);
}
}
function isShadowHost(node)
{
return window.internals.oldestShadowRoot(node);
}
function isShadowRoot(node)
{
return node instanceof window.ShadowRoot;
}
function isIframeElement(element)
{
return element && element.nodeName == 'IFRAME';
}
// You can spefify youngerShadowRoot by consecutive slashes.
// See LayoutTests/fast/dom/shadow/get-element-by-id-in-shadow-root.html for actual usages.
function getNodeInComposedTree(path)
{
var ids = path.split('/');
var node = document.getElementById(ids[0]);
for (var i = 1; node != null && i < ids.length; ++i) {
if (isIframeElement(node)) {
node = node.contentDocument.getElementById(ids[i]);
continue;
}
if (isShadowRoot(node))
node = internals.youngerShadowRoot(node);
else if (internals.oldestShadowRoot(node))
node = internals.oldestShadowRoot(node);
else
return null;
if (ids[i] != '')
node = node.getElementById(ids[i]);
}
return node;
}
function dumpNode(node)
{
if (!node)
return 'null';
if (node.id)
return '#' + node.id;
return '' + node;
}
function dumpNodeList(nodeList) {
var result = "";
var length = nodeList.length;
for (var i = 0; i < length; i++)
result += dumpNode(nodeList[i]) + ", ";
result += "length: " + length;
return result;
}
function shouldBeEqualAsArray(nodeList, expectedNodes)
{
// FIXME: Avoid polluting the global namespace.
window.nodeList = nodeList;
window.expectedNodes = expectedNodes;
shouldBe("nodeList.length", "expectedNodes.length");
for (var i = 0; i < nodeList.length; ++i) {
shouldBe("nodeList.item(" + i + ")", "expectedNodes[" + i + "]");
}
}
function innermostActiveElement(element)
{
element = element || document.activeElement;
if (isIframeElement(element)) {
if (element.contentDocument.activeElement)
return innermostActiveElement(element.contentDocument.activeElement);
return element;
}
if (isShadowHost(element)) {
var shadowRoot = window.internals.oldestShadowRoot(element);
while (shadowRoot) {
if (shadowRoot.activeElement)
return innermostActiveElement(shadowRoot.activeElement);
shadowRoot = window.internals.youngerShadowRoot(shadowRoot);
}
}
return element;
}
function isInnermostActiveElement(id)
{
var element = getNodeInComposedTree(id);
if (!element) {
debug('FAIL: There is no such element with id: '+ from);
return false;
}
if (element == innermostActiveElement())
return true;
debug('Expected innermost activeElement is ' + id + ', but actual innermost activeElement is ' + dumpNode(innermostActiveElement()));
return false;
}
function shouldNavigateFocus(from, to, direction)
{
debug('Should move from ' + from + ' to ' + to + ' in ' + direction);
var fromElement = getNodeInComposedTree(from);
if (!fromElement) {
debug('FAIL: There is no such element with id: '+ from);
return;
}
fromElement.focus();
if (!isInnermostActiveElement(from)) {
debug('FAIL: Can not be focused: '+ from);
return;
}
if (direction == 'forward')
navigateFocusForward();
else
navigateFocusBackward();
if (isInnermostActiveElement(to))
debug('PASS');
else
debug('FAIL');
}
function navigateFocusForward()
{
eventSender.keyDown('\t');
}
function navigateFocusBackward()
{
eventSender.keyDown('\t', ['shiftKey']);
}
function testFocusNavigationForward(elements)
{
for (var i = 0; i + 1 < elements.length; ++i)
shouldNavigateFocus(elements[i], elements[i + 1], 'forward');
}
function testFocusNavigationBackward(elements)
{
for (var i = 0; i + 1 < elements.length; ++i)
shouldNavigateFocus(elements[i], elements[i + 1], 'backward');
}
function dumpFlatTree(node, indent)
{
indent = indent || "";
var output = indent + dumpNode(node) + "\n";
var child;
for (child = internals.firstChildInFlatTree(node); child; child = internals.nextSiblingInFlatTree(child))
output += dumpFlatTree(child, indent + "\t");
return output;
}
function lastNodeInFlatTree(root)
{
var lastNode = root;
while (internals.lastChildInFlatTree(lastNode))
lastNode = internals.lastChildInFlatTree(lastNode);
return lastNode;
}
function showFlatTreeByTraversingInForward(root)
{
var node = root;
var last = lastNodeInFlatTree(root);
while (node) {
debug(dumpNode(node));
if (node == last)
break;
node = internals.nextInFlatTree(node);
}
}
function showFlatTreeByTraversingInBackward(root)
{
var node = lastNodeInFlatTree(root);
while (node) {
debug(dumpNode(node));
if (node == root)
break;
node = internals.previousInFlatTree(node);
}
}
function showFlatTree(node)
{
debug('Flat Tree:');
debug(dumpFlatTree(node));
debug('Traverse in forward.');
showFlatTreeByTraversingInForward(node);
debug('Traverse in backward.');
showFlatTreeByTraversingInBackward(node);
debug('');
}
function showNextNode(node)
{
var next = internals.nextInFlatTree(node);
debug('Next node of [' + dumpNode(node) + '] is [' + dumpNode(next) + ']');
}
function backgroundColorOf(selector)
{
return window.getComputedStyle(getNodeInComposedTree(selector)).backgroundColor;
}
function backgroundColorShouldBe(selector, expected)
{
shouldBeEqualToString('backgroundColorOf(\'' + selector + '\')', expected);
}
function backgroundColorShouldNotBe(selector, color)
{
var text = 'backgroundColorOf(\'' + selector + '\')';
var unevaledString = '"' + color.replace(/\\/g, "\\\\").replace(/"/g, "\"") + '"';
shouldNotBe(text, unevaledString);
}
function getElementByIdConsideringShadowDOM(root, id) {
function iter(root, id) {
if (!root)
return null;
if (root.id == id)
return root;
// We don't collect div having a shadow root, since we cannot point it correctly.
// Such div should have an inner div to be pointed correctly.
for (var child = root.firstChild; child; child = child.nextSibling) {
var node = iter(child, id);
if (node != null)
return node;
}
if (root.nodeType != 1)
return null;
for (var shadowRoot = internals.youngestShadowRoot(root); shadowRoot; shadowRoot = shadowRoot.olderShadowRoot) {
var node = iter(shadowRoot, id);
if (node != null)
return node;
}
return null;
};
if (!window.internals)
return null;
return iter(root, id);
}