| 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); |
| } |