|
| 1 | +<!DOCTYPE html> |
| 2 | +<link rel= author href=" mailto:[email protected]" > |
| 3 | +<link rel=help href="https://github.com/whatwg/html/issues/9799"> |
| 4 | +<script src="/resources/testharness.js"></script> |
| 5 | +<script src="/resources/testharnessreport.js"></script> |
| 6 | + |
| 7 | +<style> |
| 8 | +select, ::picker(select) { |
| 9 | + appearance: base-select; |
| 10 | +} |
| 11 | +</style> |
| 12 | + |
| 13 | +<div id=target> |
| 14 | + <script> |
| 15 | + window.selectedcontentRecords = []; |
| 16 | + window.selectedcontentObserver = new MutationObserver(mutations => { |
| 17 | + window.selectedcontentRecords = window.selectedcontentRecords.concat(mutations); |
| 18 | + }); |
| 19 | + const config = {attributes: true, childList: true, subtree: true}; |
| 20 | + window.selectedcontentObserver.observe(document.getElementById('target'), config); |
| 21 | + </script> |
| 22 | + |
| 23 | + <select> |
| 24 | + <button> |
| 25 | + <selectedcontent></selectedcontent> |
| 26 | + </button> |
| 27 | + <option><span>span</span> one</option> |
| 28 | + <option><span>span</span> two</option> |
| 29 | + <option selected><span>span</option> three</option> |
| 30 | + </select> |
| 31 | +</div> |
| 32 | + |
| 33 | +<script> |
| 34 | +function getNodeRepresentation(node) { |
| 35 | + if (!node) { |
| 36 | + return 'null'; |
| 37 | + } |
| 38 | + switch (node.nodeType) { |
| 39 | + case Node.ELEMENT_NODE: |
| 40 | + let representation = node.tagName.toLowerCase(); |
| 41 | + if (node.id) { |
| 42 | + representation += `#${node.id}`; |
| 43 | + } |
| 44 | + if (node.classList && node.classList.length > 0) { |
| 45 | + representation += `.${Array.from(node.classList).join('.')}`; |
| 46 | + } |
| 47 | + return representation; |
| 48 | + case Node.TEXT_NODE: |
| 49 | + const text = node.textContent.trim(); |
| 50 | + return `#text: "${text.length > 50 ? text.substring(0, 47) + '...' : text}"`; |
| 51 | + case Node.COMMENT_NODE: |
| 52 | + return ''; |
| 53 | + default: |
| 54 | + return `[Node type ${node.nodeType}]`; |
| 55 | + } |
| 56 | +} |
| 57 | + |
| 58 | +function mutationRecordToString(record) { |
| 59 | + if (!record) { |
| 60 | + return '[Invalid MutationRecord]'; |
| 61 | + } |
| 62 | + |
| 63 | + const targetStr = getNodeRepresentation(record.target); |
| 64 | + let summary = `Type: ${record.type} | Target: ${targetStr}`; |
| 65 | + |
| 66 | + switch (record.type) { |
| 67 | + case 'attributes': |
| 68 | + const attrName = record.attributeName; |
| 69 | + const oldValue = record.oldValue !== null ? `"${record.oldValue}"` : 'null'; |
| 70 | + const newValue = record.target.getAttribute(attrName); |
| 71 | + const newValueStr = newValue !== null ? `"${newValue}"` : 'null'; |
| 72 | + summary += ` | Attribute: '${attrName}' changed from ${oldValue} to ${newValueStr}`; |
| 73 | + if (record.attributeNamespace) { |
| 74 | + summary += ` (Namespace: ${record.attributeNamespace})`; |
| 75 | + } |
| 76 | + break; |
| 77 | + |
| 78 | + case 'characterData': |
| 79 | + const oldText = record.oldValue !== null ? `"${record.oldValue}"` : 'null'; |
| 80 | + const newText = record.target.textContent !== null ? `"${record.target.textContent}"` : 'null'; |
| 81 | + summary += ` | Data changed from ${oldText} to ${newText}`; |
| 82 | + break; |
| 83 | + |
| 84 | + case 'childList': |
| 85 | + if (record.addedNodes.length > 0) { |
| 86 | + const added = Array.from(record.addedNodes).map(getNodeRepresentation).join(', '); |
| 87 | + summary += ` | Added: [${added}]`; |
| 88 | + } |
| 89 | + if (record.removedNodes.length > 0) { |
| 90 | + const removed = Array.from(record.removedNodes).map(getNodeRepresentation).join(', '); |
| 91 | + summary += ` | Removed: [${removed}]`; |
| 92 | + } |
| 93 | + if (record.previousSibling) { |
| 94 | + summary += ` | After: ${getNodeRepresentation(record.previousSibling)}`; |
| 95 | + } |
| 96 | + if (record.nextSibling) { |
| 97 | + summary += ` | Before: ${getNodeRepresentation(record.nextSibling)}`; |
| 98 | + } |
| 99 | + break; |
| 100 | + |
| 101 | + default: |
| 102 | + summary += ' | [Unknown mutation type]'; |
| 103 | + break; |
| 104 | + } |
| 105 | + |
| 106 | + return summary; |
| 107 | +} |
| 108 | + |
| 109 | +function convertMutationRecords(records) { |
| 110 | + const output = []; |
| 111 | + for (const record of records) { |
| 112 | + output.push(mutationRecordToString(record)); |
| 113 | + } |
| 114 | + return output; |
| 115 | +} |
| 116 | + |
| 117 | +test(() => { |
| 118 | + const expectedMutations = [ |
| 119 | + "Type: childList | Target: div#target | Added: [#text: \"\"] | After: script", |
| 120 | + "Type: childList | Target: div#target | Added: [select] | After: #text: \"\"", |
| 121 | + "Type: childList | Target: select | Added: [#text: \"\"]", |
| 122 | + "Type: childList | Target: select | Added: [button] | After: #text: \"\"", |
| 123 | + "Type: childList | Target: button | Added: [#text: \"\"]", |
| 124 | + "Type: childList | Target: button | Added: [selectedcontent] | After: #text: \"\"", |
| 125 | + "Type: childList | Target: button | Added: [#text: \"\"] | After: selectedcontent", |
| 126 | + "Type: childList | Target: select | Added: [#text: \"\"] | After: button", |
| 127 | + "Type: childList | Target: select | Added: [option] | After: #text: \"\"", |
| 128 | + "Type: childList | Target: option | Added: [span]", |
| 129 | + "Type: childList | Target: span | Added: [#text: \"span\"]", |
| 130 | + "Type: childList | Target: option | Added: [#text: \"one\"] | After: span", |
| 131 | + "Type: childList | Target: selectedcontent | Added: [span, #text: \"one\"]", |
| 132 | + "Type: childList | Target: select | Added: [#text: \"\"] | After: option", |
| 133 | + "Type: childList | Target: select | Added: [option] | After: #text: \"\"", |
| 134 | + "Type: childList | Target: option | Added: [span]", |
| 135 | + "Type: childList | Target: span | Added: [#text: \"span\"]", |
| 136 | + "Type: childList | Target: option | Added: [#text: \"two\"] | After: span", |
| 137 | + "Type: childList | Target: select | Added: [#text: \"\"] | After: option", |
| 138 | + "Type: childList | Target: select | Added: [option] | After: #text: \"\"", |
| 139 | + "Type: childList | Target: selectedcontent | Removed: [span, #text: \"one\"]", |
| 140 | + "Type: childList | Target: option | Added: [span]", |
| 141 | + "Type: childList | Target: span | Added: [#text: \"span\"]", |
| 142 | + "Type: childList | Target: selectedcontent | Added: [span]", |
| 143 | + "Type: childList | Target: select | Added: [#text: \"three\"] | After: option", |
| 144 | + "Type: childList | Target: div#target | Added: [#text: \"\"] | After: select" |
| 145 | + ]; |
| 146 | + assert_array_equals(convertMutationRecords(window.selectedcontentRecords), expectedMutations); |
| 147 | +}, 'MutationObserver records during parsing of <select> with <selectedcontent>'); |
| 148 | +</script> |
0 commit comments