blob: 0f78c0dbc283efbbfa4bd6a6b8c6775a10d62f0b [file] [log] [blame]
<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>