| <script> |
| class DOMAgent { |
| constructor(delegate) { |
| this.enabled = false; |
| this.delegate_ = delegate; |
| this.nextNodeId_ = 1; |
| this.nodeToId_ = new Map(); |
| this.idToNode_ = new Map(); |
| this.observer_ = null; |
| Object.preventExtensions(this); |
| } |
| |
| getIdForNode_(node) { |
| if (this.nodeToId_.has(node)) |
| return this.nodeToId_.get(node); |
| var id = this.nextNodeId_++; |
| this.nodeToId_.set(node, id); |
| this.idToNode_.set(id, node); |
| return id; |
| } |
| |
| getNodeForId(nodeId) { |
| return this.idToNode_.get(nodeId); |
| } |
| |
| serializeChildren_(node) { |
| var children = []; |
| for (var child = node.firstChild; child; child = child.nextSibling) { |
| var record = this.serializeNode_(child); |
| if (record) |
| children.push(record); |
| } |
| return children; |
| } |
| |
| serializeAttributes_(element) { |
| var attributes = []; |
| var attrs = element.getAttributes(); |
| for (var i = 0; i < attrs.length; ++i) { |
| var attr = attrs[i]; |
| attributes.push(attr.name); |
| attributes.push(attr.value); |
| } |
| return attributes; |
| } |
| |
| serializeNode_(node) { |
| var id = this.getIdForNode_(node); |
| |
| var record = { |
| nodeId: id, |
| }; |
| |
| var isContainer = false; |
| |
| if (node instanceof Element) { |
| isContainer = true; |
| record.nodeType = 1; |
| record.nodeName = node.tagName; |
| record.localName = node.tagName; |
| record.nodeValue = ""; |
| record.attributes = this.serializeAttributes_(node); |
| } else if (node instanceof Text) { |
| record.nodeType = 3; |
| record.nodeName = "#text"; |
| var nodeValue = node.data; |
| if (!nodeValue.trim()) |
| return null; |
| record.nodeValue = nodeValue; |
| } else if (node instanceof Document) { |
| isContainer = true; |
| record.nodeType = 9; |
| record.nodeName = "#document"; |
| record.localName = ""; |
| record.nodeValue = ""; |
| record.documentURL = node.URL; |
| record.baseURL = node.baseURI; |
| } else if (node instanceof DocumentFragment) { |
| isContainer = true; |
| record.nodeType = 11; |
| record.nodeName = "#document-fragment"; |
| record.localName = ""; |
| record.nodeValue = ""; |
| } else { |
| console.log("Unknown node type"); |
| return null; |
| } |
| |
| if (isContainer) { |
| var children = this.serializeChildren_(node); |
| if (children.length) { |
| record.childNodeCount = children.length; |
| record.children = children; |
| } |
| } |
| |
| return record; |
| } |
| |
| enable() { |
| this.enabled = true; |
| this.observer_ = new MutationObserver(this.mutationCallback_.bind(this)); |
| this.observer_.observe(document, { |
| childList: true, |
| attributes: true, |
| characterData: true, |
| subtree : true, |
| }); |
| } |
| |
| getDocument() { |
| return { |
| root: this.serializeNode_(document), |
| }; |
| } |
| |
| hideHighlight() { |
| } |
| |
| highlightNode() { |
| } |
| |
| mutationCallback_(mutationRecords) { |
| for (var i = 0; i < mutationRecords.length; ++i) { |
| var record = mutationRecords[i]; |
| var type = record.type; |
| var target = record.target; |
| var nodeId = this.getIdForNode_(target); |
| if (type == "attributes") { |
| var attributeName = record.attributeName; |
| if (target.hasAttribute(attributeName)) { |
| this.delegate_.sendMessage("DOM.attributeModified", { |
| nodeId: nodeId, |
| name: attributeName, |
| value: target.getAttribute(attributeName), |
| }); |
| } else { |
| this.delegate_.sendMessage("DOM.attributeRemoved", { |
| nodeId: nodeId, |
| name: attributeName, |
| }); |
| } |
| } else if (type == "characterData") { |
| this.delegate_.sendMessage("DOM.characterDataModified", { |
| nodeId: nodeId, |
| characterData: target.data, |
| }); |
| } else if (type == "childList") { |
| // FIXME: If this subtree isn't expanded, we only need to send across the |
| // {"method":"DOM.childNodeCountUpdated","params":"nodeId":648,"childNodeCount":2} |
| |
| Array.prototype.forEach.call(record.removedNodes, function(node) { |
| this.delegate_.sendMessage("DOM.childNodeRemoved", { |
| parentNodeId: nodeId, |
| nodeId: this.getIdForNode_(node), |
| }); |
| }.bind(this)); |
| |
| Array.prototype.forEach.call(record.addedNodes, function(node) { |
| var previousNodeId = node.previousSibling ? this.getIdForNode_(node.previousSibling) : 0; |
| this.delegate_.sendMessage("DOM.childNodeInserted", { |
| parentNodeId: nodeId, |
| previousNodeId: previousNodeId, |
| node: this.serializeNode_(node), |
| }); |
| }.bind(this)); |
| } |
| } |
| } |
| } |
| |
| module.exports = DOMAgent; |
| </script> |