2 * Copyright (C) 2009, 2010 Google Inc. All rights reserved.
3 * Copyright (C) 2009 Joseph Pecoraro
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 * @param {!WebInspector.DOMModel} domModel
35 * @param {?WebInspector.DOMDocument} doc
36 * @param {boolean} isInShadowTree
37 * @param {!DOMAgent.Node} payload
39 WebInspector.DOMNode = function(domModel, doc, isInShadowTree, payload) {
40 this._domModel = domModel;
41 this.ownerDocument = doc;
42 this._isInShadowTree = isInShadowTree;
44 this.id = payload.nodeId;
45 domModel._idToDOMNode[this.id] = this;
46 this._nodeType = payload.nodeType;
47 this._nodeName = payload.nodeName;
48 this._localName = payload.localName;
49 this._nodeValue = payload.nodeValue;
50 this._pseudoType = payload.pseudoType;
51 this._shadowRootType = payload.shadowRootType;
52 this._frameId = payload.frameId || null;
54 this._shadowRoots = [];
56 this._attributes = [];
57 this._attributesMap = {};
58 if (payload.attributes)
59 this._setAttributesPayload(payload.attributes);
61 this._userProperties = {};
62 this._descendantUserPropertyCounters = {};
64 this._childNodeCount = payload.childNodeCount || 0;
65 this._children = null;
67 this.nextSibling = null;
68 this.previousSibling = null;
69 this.firstChild = null;
70 this.lastChild = null;
71 this.parentNode = null;
73 if (payload.shadowRoots) {
74 for (var i = 0; i < payload.shadowRoots.length; ++i) {
75 var root = payload.shadowRoots[i];
76 var node = new WebInspector.DOMNode(this._domModel, this.ownerDocument, true, root);
77 this._shadowRoots.push(node);
78 node.parentNode = this;
82 if (payload.templateContent) {
83 this._templateContent = new WebInspector.DOMNode(this._domModel, this.ownerDocument, true, payload.templateContent);
84 this._templateContent.parentNode = this;
87 if (payload.importedDocument) {
88 this._importedDocument = new WebInspector.DOMNode(this._domModel, this.ownerDocument, true, payload.importedDocument);
89 this._importedDocument.parentNode = this;
93 this._setChildrenPayload(payload.children);
95 this._setPseudoElements(payload.pseudoElements);
97 if (payload.contentDocument) {
98 this._contentDocument = new WebInspector.DOMDocument(domModel, payload.contentDocument);
99 this._children = [this._contentDocument];
103 if (this._nodeType === Node.ELEMENT_NODE) {
104 // HTML and BODY from internal iframes should not overwrite top-level ones.
105 if (this.ownerDocument && !this.ownerDocument.documentElement && this._nodeName === "HTML")
106 this.ownerDocument.documentElement = this;
107 if (this.ownerDocument && !this.ownerDocument.body && this._nodeName === "BODY")
108 this.ownerDocument.body = this;
109 } else if (this._nodeType === Node.DOCUMENT_TYPE_NODE) {
110 this.publicId = payload.publicId;
111 this.systemId = payload.systemId;
112 this.internalSubset = payload.internalSubset;
113 } else if (this._nodeType === Node.ATTRIBUTE_NODE) {
114 this.name = payload.name;
115 this.value = payload.value;
122 WebInspector.DOMNode.PseudoElementNames = {
130 WebInspector.DOMNode.ShadowRootTypes = {
131 UserAgent: "user-agent",
135 WebInspector.DOMNode.prototype = {
137 * @return {?Array.<!WebInspector.DOMNode>}
141 return this._children ? this._children.slice() : null;
147 hasAttributes: function()
149 return this._attributes.length > 0;
155 childNodeCount: function()
157 return this._childNodeCount;
163 hasShadowRoots: function()
165 return !!this._shadowRoots.length;
169 * @return {!Array.<!WebInspector.DOMNode>}
171 shadowRoots: function()
173 return this._shadowRoots.slice();
177 * @return {?WebInspector.DOMNode}
179 templateContent: function()
181 return this._templateContent;
185 * @return {?WebInspector.DOMNode}
187 importedDocument: function()
189 return this._importedDocument;
197 return this._nodeType;
205 return this._nodeName;
209 * @return {string|undefined}
211 pseudoType: function()
213 return this._pseudoType;
219 hasPseudoElements: function()
221 return Object.keys(this._pseudoElements).length !== 0;
225 * @return {!Object.<string, !WebInspector.DOMNode>}
227 pseudoElements: function()
229 return this._pseudoElements;
235 isInShadowTree: function()
237 return this._isInShadowTree;
241 * @return {?WebInspector.DOMNode}
243 ancestorUserAgentShadowRoot: function()
245 if (!this._isInShadowTree)
249 while (!current.isShadowRoot())
250 current = current.parentNode;
251 return current.shadowRootType() === WebInspector.DOMNode.ShadowRootTypes.UserAgent ? current : null;
257 isShadowRoot: function()
259 return !!this._shadowRootType;
265 shadowRootType: function()
267 return this._shadowRootType || null;
273 nodeNameInCorrectCase: function()
275 var shadowRootType = this.shadowRootType();
277 return "#shadow-root" + (shadowRootType === WebInspector.DOMNode.ShadowRootTypes.UserAgent ? " (user-agent)" : "");
278 return this.isXMLNode() ? this.nodeName() : this.nodeName().toLowerCase();
282 * @param {string} name
283 * @param {function(?Protocol.Error)=} callback
285 setNodeName: function(name, callback)
287 DOMAgent.setNodeName(this.id, name, WebInspector.domModel._markRevision(this, callback));
293 localName: function()
295 return this._localName;
301 nodeValue: function()
303 return this._nodeValue;
307 * @param {string} value
308 * @param {function(?Protocol.Error)=} callback
310 setNodeValue: function(value, callback)
312 DOMAgent.setNodeValue(this.id, value, WebInspector.domModel._markRevision(this, callback));
316 * @param {string} name
319 getAttribute: function(name)
321 var attr = this._attributesMap[name];
322 return attr ? attr.value : undefined;
326 * @param {string} name
327 * @param {string} text
328 * @param {function(?Protocol.Error)=} callback
330 setAttribute: function(name, text, callback)
332 DOMAgent.setAttributesAsText(this.id, text, name, WebInspector.domModel._markRevision(this, callback));
336 * @param {string} name
337 * @param {string} value
338 * @param {function(?Protocol.Error)=} callback
340 setAttributeValue: function(name, value, callback)
342 DOMAgent.setAttributeValue(this.id, name, value, WebInspector.domModel._markRevision(this, callback));
348 attributes: function()
350 return this._attributes;
354 * @param {string} name
355 * @param {function(?Protocol.Error)=} callback
357 removeAttribute: function(name, callback)
360 * @param {?Protocol.Error} error
361 * @this {WebInspector.DOMNode}
363 function mycallback(error)
366 delete this._attributesMap[name];
367 for (var i = 0; i < this._attributes.length; ++i) {
368 if (this._attributes[i].name === name) {
369 this._attributes.splice(i, 1);
375 WebInspector.domModel._markRevision(this, callback)(error);
377 DOMAgent.removeAttribute(this.id, name, mycallback.bind(this));
381 * @param {function(?Array.<!WebInspector.DOMNode>)=} callback
383 getChildNodes: function(callback)
385 if (this._children) {
387 callback(this.children());
392 * @this {WebInspector.DOMNode}
393 * @param {?Protocol.Error} error
395 function mycallback(error)
398 callback(error ? null : this.children());
401 DOMAgent.requestChildNodes(this.id, undefined, mycallback.bind(this));
405 * @param {number} depth
406 * @param {function(?Array.<!WebInspector.DOMNode>)=} callback
408 getSubtree: function(depth, callback)
411 * @this {WebInspector.DOMNode}
412 * @param {?Protocol.Error} error
414 function mycallback(error)
417 callback(error ? null : this._children);
420 DOMAgent.requestChildNodes(this.id, depth, mycallback.bind(this));
424 * @param {function(?Protocol.Error)=} callback
426 getOuterHTML: function(callback)
428 DOMAgent.getOuterHTML(this.id, callback);
432 * @param {string} html
433 * @param {function(?Protocol.Error)=} callback
435 setOuterHTML: function(html, callback)
437 DOMAgent.setOuterHTML(this.id, html, WebInspector.domModel._markRevision(this, callback));
441 * @param {function(?Protocol.Error, !DOMAgent.NodeId=)=} callback
443 removeNode: function(callback)
445 DOMAgent.removeNode(this.id, WebInspector.domModel._markRevision(this, callback));
450 function copy(error, text)
453 InspectorFrontendHost.copyText(text);
455 DOMAgent.getOuterHTML(this.id, copy);
459 * @param {string} objectGroupId
460 * @param {function(?Protocol.Error)=} callback
462 eventListeners: function(objectGroupId, callback)
464 DOMAgent.getEventListenersForNode(this.id, objectGroupId, callback);
473 * @param {?WebInspector.DOMNode} node
475 function canPush(node)
477 return node && ("index" in node || (node.isShadowRoot() && node.parentNode)) && node._nodeName.length;
482 while (canPush(node)) {
483 var index = typeof node.index === "number" ? node.index : (node.shadowRootType() === WebInspector.DOMNode.ShadowRootTypes.UserAgent ? "u" : "a");
484 path.push([index, node._nodeName]);
485 node = node.parentNode;
488 return path.join(",");
492 * @param {!WebInspector.DOMNode} node
495 isAncestor: function(node)
500 var currentNode = node.parentNode;
501 while (currentNode) {
502 if (this === currentNode)
504 currentNode = currentNode.parentNode;
510 * @param {!WebInspector.DOMNode} descendant
513 isDescendant: function(descendant)
515 return descendant !== null && descendant.isAncestor(this);
519 * @return {?PageAgent.FrameId}
524 while (!node._frameId && node.parentNode)
525 node = node.parentNode;
526 return node._frameId;
530 * @param {!Array.<string>} attrs
533 _setAttributesPayload: function(attrs)
535 var attributesChanged = !this._attributes || attrs.length !== this._attributes.length * 2;
536 var oldAttributesMap = this._attributesMap || {};
538 this._attributes = [];
539 this._attributesMap = {};
541 for (var i = 0; i < attrs.length; i += 2) {
543 var value = attrs[i + 1];
544 this._addAttribute(name, value);
546 if (attributesChanged)
549 if (!oldAttributesMap[name] || oldAttributesMap[name].value !== value)
550 attributesChanged = true;
552 return attributesChanged;
556 * @param {!WebInspector.DOMNode} prev
557 * @param {!DOMAgent.Node} payload
558 * @return {!WebInspector.DOMNode}
560 _insertChild: function(prev, payload)
562 var node = new WebInspector.DOMNode(this._domModel, this.ownerDocument, this._isInShadowTree, payload);
563 this._children.splice(this._children.indexOf(prev) + 1, 0, node);
569 * @param {!WebInspector.DOMNode} node
571 _removeChild: function(node)
573 if (node.pseudoType()) {
574 delete this._pseudoElements[node.pseudoType()];
576 var shadowRootIndex = this._shadowRoots.indexOf(node);
577 if (shadowRootIndex !== -1)
578 this._shadowRoots.splice(shadowRootIndex, 1);
580 this._children.splice(this._children.indexOf(node), 1);
582 node.parentNode = null;
583 node._updateChildUserPropertyCountsOnRemoval(this);
588 * @param {!Array.<!DOMAgent.Node>} payloads
590 _setChildrenPayload: function(payloads)
592 // We set children in the constructor.
593 if (this._contentDocument)
597 for (var i = 0; i < payloads.length; ++i) {
598 var payload = payloads[i];
599 var node = new WebInspector.DOMNode(this._domModel, this.ownerDocument, this._isInShadowTree, payload);
600 this._children.push(node);
606 * @param {!Array.<!DOMAgent.Node>|undefined} payloads
608 _setPseudoElements: function(payloads)
610 this._pseudoElements = {};
614 for (var i = 0; i < payloads.length; ++i) {
615 var node = new WebInspector.DOMNode(this._domModel, this.ownerDocument, this._isInShadowTree, payloads[i]);
616 node.parentNode = this;
617 this._pseudoElements[node.pseudoType()] = node;
621 _renumber: function()
623 this._childNodeCount = this._children.length;
624 if (this._childNodeCount == 0) {
625 this.firstChild = null;
626 this.lastChild = null;
629 this.firstChild = this._children[0];
630 this.lastChild = this._children[this._childNodeCount - 1];
631 for (var i = 0; i < this._childNodeCount; ++i) {
632 var child = this._children[i];
634 child.nextSibling = i + 1 < this._childNodeCount ? this._children[i + 1] : null;
635 child.previousSibling = i - 1 >= 0 ? this._children[i - 1] : null;
636 child.parentNode = this;
641 * @param {string} name
642 * @param {string} value
644 _addAttribute: function(name, value)
651 this._attributesMap[name] = attr;
652 this._attributes.push(attr);
656 * @param {string} name
657 * @param {string} value
659 _setAttribute: function(name, value)
661 var attr = this._attributesMap[name];
665 this._addAttribute(name, value);
669 * @param {string} name
671 _removeAttribute: function(name)
673 var attr = this._attributesMap[name];
675 this._attributes.remove(attr);
676 delete this._attributesMap[name];
681 * @param {!WebInspector.DOMNode} targetNode
682 * @param {?WebInspector.DOMNode} anchorNode
683 * @param {function(?Protocol.Error, !DOMAgent.NodeId=)=} callback
685 moveTo: function(targetNode, anchorNode, callback)
687 DOMAgent.moveTo(this.id, targetNode.id, anchorNode ? anchorNode.id : undefined, WebInspector.domModel._markRevision(this, callback));
693 isXMLNode: function()
695 return !!this.ownerDocument && !!this.ownerDocument.xmlVersion;
698 _updateChildUserPropertyCountsOnRemoval: function(parentNode)
701 if (this._userProperties) {
702 for (var name in this._userProperties)
703 result[name] = (result[name] || 0) + 1;
706 if (this._descendantUserPropertyCounters) {
707 for (var name in this._descendantUserPropertyCounters) {
708 var counter = this._descendantUserPropertyCounters[name];
709 result[name] = (result[name] || 0) + counter;
713 for (var name in result)
714 parentNode._updateDescendantUserPropertyCount(name, -result[name]);
717 _updateDescendantUserPropertyCount: function(name, delta)
719 if (!this._descendantUserPropertyCounters.hasOwnProperty(name))
720 this._descendantUserPropertyCounters[name] = 0;
721 this._descendantUserPropertyCounters[name] += delta;
722 if (!this._descendantUserPropertyCounters[name])
723 delete this._descendantUserPropertyCounters[name];
725 this.parentNode._updateDescendantUserPropertyCount(name, delta);
728 setUserProperty: function(name, value)
730 if (value === null) {
731 this.removeUserProperty(name);
735 if (this.parentNode && !this._userProperties.hasOwnProperty(name))
736 this.parentNode._updateDescendantUserPropertyCount(name, 1);
738 this._userProperties[name] = value;
741 removeUserProperty: function(name)
743 if (!this._userProperties.hasOwnProperty(name))
746 delete this._userProperties[name];
748 this.parentNode._updateDescendantUserPropertyCount(name, -1);
752 * @param {string} name
756 getUserProperty: function(name)
758 return (this._userProperties && this._userProperties[name]) || null;
762 * @param {string} name
765 descendantUserPropertyCount: function(name)
767 return this._descendantUserPropertyCounters && this._descendantUserPropertyCounters[name] ? this._descendantUserPropertyCounters[name] : 0;
771 * @param {string} url
774 resolveURL: function(url)
778 for (var frameOwnerCandidate = this; frameOwnerCandidate; frameOwnerCandidate = frameOwnerCandidate.parentNode) {
779 if (frameOwnerCandidate.baseURL)
780 return WebInspector.ParsedURL.completeURL(frameOwnerCandidate.baseURL, url);
787 * @extends {WebInspector.DOMNode}
789 * @param {!WebInspector.DOMModel} domModel
790 * @param {!DOMAgent.Node} payload
792 WebInspector.DOMDocument = function(domModel, payload)
794 WebInspector.DOMNode.call(this, domModel, this, false, payload);
795 this.documentURL = payload.documentURL || "";
796 this.baseURL = payload.baseURL || "";
797 this.xmlVersion = payload.xmlVersion;
798 this._listeners = {};
801 WebInspector.DOMDocument.prototype = {
802 __proto__: WebInspector.DOMNode.prototype
806 * @extends {WebInspector.Object}
809 WebInspector.DOMModel = function() {
810 /** @type {!Object.<number, !WebInspector.DOMNode>} */
811 this._idToDOMNode = {};
812 /** @type {?WebInspector.DOMDocument} */
813 this._document = null;
814 /** @type {!Object.<number, boolean>} */
815 this._attributeLoadNodeIds = {};
816 InspectorBackend.registerDOMDispatcher(new WebInspector.DOMDispatcher(this));
818 this._defaultHighlighter = new WebInspector.DefaultDOMNodeHighlighter();
819 this._highlighter = this._defaultHighlighter;
822 WebInspector.DOMModel.Events = {
823 AttrModified: "AttrModified",
824 AttrRemoved: "AttrRemoved",
825 CharacterDataModified: "CharacterDataModified",
826 NodeInserted: "NodeInserted",
827 NodeRemoved: "NodeRemoved",
828 DocumentUpdated: "DocumentUpdated",
829 ChildNodeCountUpdated: "ChildNodeCountUpdated",
830 UndoRedoRequested: "UndoRedoRequested",
831 UndoRedoCompleted: "UndoRedoCompleted",
834 WebInspector.DOMModel.prototype = {
836 * @param {function(!WebInspector.DOMDocument)=} callback
838 requestDocument: function(callback)
840 if (this._document) {
842 callback(this._document);
846 if (this._pendingDocumentRequestCallbacks) {
847 this._pendingDocumentRequestCallbacks.push(callback);
851 this._pendingDocumentRequestCallbacks = [callback];
854 * @this {WebInspector.DOMModel}
855 * @param {?Protocol.Error} error
856 * @param {!DOMAgent.Node} root
858 function onDocumentAvailable(error, root)
861 this._setDocument(root);
863 for (var i = 0; i < this._pendingDocumentRequestCallbacks.length; ++i) {
864 var callback = this._pendingDocumentRequestCallbacks[i];
866 callback(this._document);
868 delete this._pendingDocumentRequestCallbacks;
871 DOMAgent.getDocument(onDocumentAvailable.bind(this));
875 * @return {?WebInspector.DOMDocument}
877 existingDocument: function()
879 return this._document;
883 * @param {!RuntimeAgent.RemoteObjectId} objectId
884 * @param {function(?DOMAgent.NodeId)=} callback
886 pushNodeToFrontend: function(objectId, callback)
888 this._dispatchWhenDocumentAvailable(DOMAgent.requestNode.bind(DOMAgent, objectId), callback);
892 * @param {string} path
893 * @param {function(?number)=} callback
895 pushNodeByPathToFrontend: function(path, callback)
897 this._dispatchWhenDocumentAvailable(DOMAgent.pushNodeByPathToFrontend.bind(DOMAgent, path), callback);
901 * @param {!Array.<number>} backendNodeIds
902 * @param {function(?Array.<number>)=} callback
904 pushNodesByBackendIdsToFrontend: function(backendNodeIds, callback)
906 this._dispatchWhenDocumentAvailable(DOMAgent.pushNodesByBackendIdsToFrontend.bind(DOMAgent, backendNodeIds), callback);
910 * @param {function(!T)=} callback
911 * @return {function(?Protocol.Error, !T=)|undefined}
914 _wrapClientCallback: function(callback)
919 * @param {?Protocol.Error} error
920 * @param {!T=} result
923 return function(error, result)
925 // Caller is responsible for handling the actual error.
926 callback(error ? null : result);
931 * @param {function(function(?Protocol.Error, !T=)=)} func
932 * @param {function(!T)=} callback
935 _dispatchWhenDocumentAvailable: function(func, callback)
937 var callbackWrapper = this._wrapClientCallback(callback);
940 * @this {WebInspector.DOMModel}
942 function onDocumentAvailable()
945 func(callbackWrapper);
948 callbackWrapper("No document");
951 this.requestDocument(onDocumentAvailable.bind(this));
955 * @param {!DOMAgent.NodeId} nodeId
956 * @param {string} name
957 * @param {string} value
959 _attributeModified: function(nodeId, name, value)
961 var node = this._idToDOMNode[nodeId];
965 node._setAttribute(name, value);
966 this.dispatchEventToListeners(WebInspector.DOMModel.Events.AttrModified, { node: node, name: name });
970 * @param {!DOMAgent.NodeId} nodeId
971 * @param {string} name
973 _attributeRemoved: function(nodeId, name)
975 var node = this._idToDOMNode[nodeId];
978 node._removeAttribute(name);
979 this.dispatchEventToListeners(WebInspector.DOMModel.Events.AttrRemoved, { node: node, name: name });
983 * @param {!Array.<!DOMAgent.NodeId>} nodeIds
985 _inlineStyleInvalidated: function(nodeIds)
987 for (var i = 0; i < nodeIds.length; ++i)
988 this._attributeLoadNodeIds[nodeIds[i]] = true;
989 if ("_loadNodeAttributesTimeout" in this)
991 this._loadNodeAttributesTimeout = setTimeout(this._loadNodeAttributes.bind(this), 20);
994 _loadNodeAttributes: function()
997 * @this {WebInspector.DOMModel}
998 * @param {!DOMAgent.NodeId} nodeId
999 * @param {?Protocol.Error} error
1000 * @param {!Array.<string>} attributes
1002 function callback(nodeId, error, attributes)
1005 // We are calling _loadNodeAttributes asynchronously, it is ok if node is not found.
1008 var node = this._idToDOMNode[nodeId];
1010 if (node._setAttributesPayload(attributes))
1011 this.dispatchEventToListeners(WebInspector.DOMModel.Events.AttrModified, { node: node, name: "style" });
1015 delete this._loadNodeAttributesTimeout;
1017 for (var nodeId in this._attributeLoadNodeIds) {
1018 var nodeIdAsNumber = parseInt(nodeId, 10);
1019 DOMAgent.getAttributes(nodeIdAsNumber, callback.bind(this, nodeIdAsNumber));
1021 this._attributeLoadNodeIds = {};
1025 * @param {!DOMAgent.NodeId} nodeId
1026 * @param {string} newValue
1028 _characterDataModified: function(nodeId, newValue)
1030 var node = this._idToDOMNode[nodeId];
1031 node._nodeValue = newValue;
1032 this.dispatchEventToListeners(WebInspector.DOMModel.Events.CharacterDataModified, node);
1036 * @param {!DOMAgent.NodeId} nodeId
1037 * @return {?WebInspector.DOMNode}
1039 nodeForId: function(nodeId)
1041 return this._idToDOMNode[nodeId] || null;
1044 _documentUpdated: function()
1046 this._setDocument(null);
1050 * @param {?DOMAgent.Node} payload
1052 _setDocument: function(payload)
1054 this._idToDOMNode = {};
1055 if (payload && "nodeId" in payload)
1056 this._document = new WebInspector.DOMDocument(this, payload);
1058 this._document = null;
1059 this.dispatchEventToListeners(WebInspector.DOMModel.Events.DocumentUpdated, this._document);
1063 * @param {!DOMAgent.Node} payload
1065 _setDetachedRoot: function(payload)
1067 if (payload.nodeName === "#document")
1068 new WebInspector.DOMDocument(this, payload);
1070 new WebInspector.DOMNode(this, null, false, payload);
1074 * @param {!DOMAgent.NodeId} parentId
1075 * @param {!Array.<!DOMAgent.Node>} payloads
1077 _setChildNodes: function(parentId, payloads)
1079 if (!parentId && payloads.length) {
1080 this._setDetachedRoot(payloads[0]);
1084 var parent = this._idToDOMNode[parentId];
1085 parent._setChildrenPayload(payloads);
1089 * @param {!DOMAgent.NodeId} nodeId
1090 * @param {number} newValue
1092 _childNodeCountUpdated: function(nodeId, newValue)
1094 var node = this._idToDOMNode[nodeId];
1095 node._childNodeCount = newValue;
1096 this.dispatchEventToListeners(WebInspector.DOMModel.Events.ChildNodeCountUpdated, node);
1100 * @param {!DOMAgent.NodeId} parentId
1101 * @param {!DOMAgent.NodeId} prevId
1102 * @param {!DOMAgent.Node} payload
1104 _childNodeInserted: function(parentId, prevId, payload)
1106 var parent = this._idToDOMNode[parentId];
1107 var prev = this._idToDOMNode[prevId];
1108 var node = parent._insertChild(prev, payload);
1109 this._idToDOMNode[node.id] = node;
1110 this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeInserted, node);
1114 * @param {!DOMAgent.NodeId} parentId
1115 * @param {!DOMAgent.NodeId} nodeId
1117 _childNodeRemoved: function(parentId, nodeId)
1119 var parent = this._idToDOMNode[parentId];
1120 var node = this._idToDOMNode[nodeId];
1121 parent._removeChild(node);
1123 this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeRemoved, {node: node, parent: parent});
1127 * @param {!DOMAgent.NodeId} hostId
1128 * @param {!DOMAgent.Node} root
1130 _shadowRootPushed: function(hostId, root)
1132 var host = this._idToDOMNode[hostId];
1135 var node = new WebInspector.DOMNode(this, host.ownerDocument, true, root);
1136 node.parentNode = host;
1137 this._idToDOMNode[node.id] = node;
1138 host._shadowRoots.push(node);
1139 this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeInserted, node);
1143 * @param {!DOMAgent.NodeId} hostId
1144 * @param {!DOMAgent.NodeId} rootId
1146 _shadowRootPopped: function(hostId, rootId)
1148 var host = this._idToDOMNode[hostId];
1151 var root = this._idToDOMNode[rootId];
1154 host._removeChild(root);
1156 this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeRemoved, {node: root, parent: host});
1160 * @param {!DOMAgent.NodeId} parentId
1161 * @param {!DOMAgent.Node} pseudoElement
1163 _pseudoElementAdded: function(parentId, pseudoElement)
1165 var parent = this._idToDOMNode[parentId];
1168 var node = new WebInspector.DOMNode(this, parent.ownerDocument, false, pseudoElement);
1169 node.parentNode = parent;
1170 this._idToDOMNode[node.id] = node;
1171 console.assert(!parent._pseudoElements[node.pseudoType()]);
1172 parent._pseudoElements[node.pseudoType()] = node;
1173 this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeInserted, node);
1177 * @param {!DOMAgent.NodeId} parentId
1178 * @param {!DOMAgent.NodeId} pseudoElementId
1180 _pseudoElementRemoved: function(parentId, pseudoElementId)
1182 var parent = this._idToDOMNode[parentId];
1185 var pseudoElement = this._idToDOMNode[pseudoElementId];
1188 parent._removeChild(pseudoElement);
1189 this._unbind(pseudoElement);
1190 this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeRemoved, {node: pseudoElement, parent: parent});
1194 * @param {!WebInspector.DOMNode} node
1196 _unbind: function(node)
1198 delete this._idToDOMNode[node.id];
1199 for (var i = 0; node._children && i < node._children.length; ++i)
1200 this._unbind(node._children[i]);
1201 for (var i = 0; i < node._shadowRoots.length; ++i)
1202 this._unbind(node._shadowRoots[i]);
1203 var pseudoElements = node.pseudoElements();
1204 for (var id in pseudoElements)
1205 this._unbind(pseudoElements[id]);
1206 if (node._templateContent)
1207 this._unbind(node._templateContent);
1211 * @param {number} nodeId
1213 inspectElement: function(nodeId)
1215 WebInspector.Revealer.reveal(this.nodeForId(nodeId));
1219 * @param {!DOMAgent.NodeId} nodeId
1221 _inspectNodeRequested: function(nodeId)
1223 this.inspectElement(nodeId);
1227 * @param {string} query
1228 * @param {function(number)} searchCallback
1230 performSearch: function(query, searchCallback)
1232 this.cancelSearch();
1235 * @param {?Protocol.Error} error
1236 * @param {string} searchId
1237 * @param {number} resultsCount
1238 * @this {WebInspector.DOMModel}
1240 function callback(error, searchId, resultsCount)
1242 this._searchId = searchId;
1243 searchCallback(resultsCount);
1245 DOMAgent.performSearch(query, callback.bind(this));
1249 * @param {number} index
1250 * @param {?function(?WebInspector.DOMNode)} callback
1252 searchResult: function(index, callback)
1255 DOMAgent.getSearchResults(this._searchId, index, index + 1, searchResultsCallback.bind(this));
1260 * @param {?Protocol.Error} error
1261 * @param {!Array.<number>} nodeIds
1262 * @this {WebInspector.DOMModel}
1264 function searchResultsCallback(error, nodeIds)
1267 console.error(error);
1271 if (nodeIds.length != 1)
1274 callback(this.nodeForId(nodeIds[0]));
1278 cancelSearch: function()
1280 if (this._searchId) {
1281 DOMAgent.discardSearchResults(this._searchId);
1282 delete this._searchId;
1287 * @param {!DOMAgent.NodeId} nodeId
1288 * @param {string} selectors
1289 * @param {function(?DOMAgent.NodeId)=} callback
1291 querySelector: function(nodeId, selectors, callback)
1293 DOMAgent.querySelector(nodeId, selectors, this._wrapClientCallback(callback));
1297 * @param {!DOMAgent.NodeId} nodeId
1298 * @param {string} selectors
1299 * @param {function(!Array.<!DOMAgent.NodeId>=)=} callback
1301 querySelectorAll: function(nodeId, selectors, callback)
1303 DOMAgent.querySelectorAll(nodeId, selectors, this._wrapClientCallback(callback));
1307 * @param {!DOMAgent.NodeId=} nodeId
1308 * @param {string=} mode
1309 * @param {!RuntimeAgent.RemoteObjectId=} objectId
1311 highlightDOMNode: function(nodeId, mode, objectId)
1313 if (this._hideDOMNodeHighlightTimeout) {
1314 clearTimeout(this._hideDOMNodeHighlightTimeout);
1315 delete this._hideDOMNodeHighlightTimeout;
1317 this._highlighter.highlightDOMNode(nodeId || 0, this._buildHighlightConfig(mode), objectId);
1320 hideDOMNodeHighlight: function()
1322 this.highlightDOMNode(0);
1326 * @param {!DOMAgent.NodeId} nodeId
1328 highlightDOMNodeForTwoSeconds: function(nodeId)
1330 this.highlightDOMNode(nodeId);
1331 this._hideDOMNodeHighlightTimeout = setTimeout(this.hideDOMNodeHighlight.bind(this), 2000);
1335 * @param {boolean} enabled
1336 * @param {boolean} inspectUAShadowDOM
1337 * @param {function(?Protocol.Error)=} callback
1339 setInspectModeEnabled: function(enabled, inspectUAShadowDOM, callback)
1342 * @this {WebInspector.DOMModel}
1344 function onDocumentAvailable()
1346 this._highlighter.setInspectModeEnabled(enabled, inspectUAShadowDOM, this._buildHighlightConfig(), callback);
1348 this.requestDocument(onDocumentAvailable.bind(this));
1352 * @param {string=} mode
1353 * @return {!DOMAgent.HighlightConfig}
1355 _buildHighlightConfig: function(mode)
1357 mode = mode || "all";
1358 var highlightConfig = { showInfo: mode === "all", showRulers: WebInspector.settings.showMetricsRulers.get() };
1359 if (mode === "all" || mode === "content")
1360 highlightConfig.contentColor = WebInspector.Color.PageHighlight.Content.toProtocolRGBA();
1362 if (mode === "all" || mode === "padding")
1363 highlightConfig.paddingColor = WebInspector.Color.PageHighlight.Padding.toProtocolRGBA();
1365 if (mode === "all" || mode === "border")
1366 highlightConfig.borderColor = WebInspector.Color.PageHighlight.Border.toProtocolRGBA();
1368 if (mode === "all" || mode === "margin")
1369 highlightConfig.marginColor = WebInspector.Color.PageHighlight.Margin.toProtocolRGBA();
1372 highlightConfig.eventTargetColor = WebInspector.Color.PageHighlight.EventTarget.toProtocolRGBA();
1374 return highlightConfig;
1378 * @param {!WebInspector.DOMNode} node
1379 * @param {function(?Protocol.Error, !A=, !B=)=} callback
1380 * @return {function(?Protocol.Error, !A=, !B=)}
1383 _markRevision: function(node, callback)
1386 * @param {?Protocol.Error} error
1387 * @this {WebInspector.DOMModel}
1389 function wrapperFunction(error)
1392 this.markUndoableState();
1395 callback.apply(this, arguments);
1397 return wrapperFunction.bind(this);
1401 * @param {boolean} emulationEnabled
1403 emulateTouchEventObjects: function(emulationEnabled)
1405 const injectedFunction = function() {
1406 const touchEvents = ["ontouchstart", "ontouchend", "ontouchmove", "ontouchcancel"];
1407 var recepients = [window.__proto__, document.__proto__];
1408 for (var i = 0; i < touchEvents.length; ++i) {
1409 for (var j = 0; j < recepients.length; ++j) {
1410 if (!(touchEvents[i] in recepients[j]))
1411 Object.defineProperty(recepients[j], touchEvents[i], { value: null, writable: true, configurable: true, enumerable: true });
1416 if (emulationEnabled && !this._addTouchEventsScriptInjecting) {
1417 this._addTouchEventsScriptInjecting = true;
1418 PageAgent.addScriptToEvaluateOnLoad("(" + injectedFunction.toString() + ")()", scriptAddedCallback.bind(this));
1420 if (typeof this._addTouchEventsScriptId !== "undefined") {
1421 PageAgent.removeScriptToEvaluateOnLoad(this._addTouchEventsScriptId);
1422 delete this._addTouchEventsScriptId;
1427 * @param {?Protocol.Error} error
1428 * @param {string} scriptId
1429 * @this {WebInspector.DOMModel}
1431 function scriptAddedCallback(error, scriptId)
1433 delete this._addTouchEventsScriptInjecting;
1436 this._addTouchEventsScriptId = scriptId;
1439 PageAgent.setTouchEmulationEnabled(emulationEnabled);
1442 markUndoableState: function()
1444 DOMAgent.markUndoableState();
1448 * @param {function(?Protocol.Error)=} callback
1450 undo: function(callback)
1453 * @param {?Protocol.Error} error
1454 * @this {WebInspector.DOMModel}
1456 function mycallback(error)
1458 this.dispatchEventToListeners(WebInspector.DOMModel.Events.UndoRedoCompleted);
1462 this.dispatchEventToListeners(WebInspector.DOMModel.Events.UndoRedoRequested);
1463 DOMAgent.undo(callback);
1467 * @param {function(?Protocol.Error)=} callback
1469 redo: function(callback)
1472 * @param {?Protocol.Error} error
1473 * @this {WebInspector.DOMModel}
1475 function mycallback(error)
1477 this.dispatchEventToListeners(WebInspector.DOMModel.Events.UndoRedoCompleted);
1481 this.dispatchEventToListeners(WebInspector.DOMModel.Events.UndoRedoRequested);
1482 DOMAgent.redo(callback);
1486 * @param {?WebInspector.DOMNodeHighlighter} highlighter
1488 setHighlighter: function(highlighter)
1490 this._highlighter = highlighter || this._defaultHighlighter;
1493 __proto__: WebInspector.Object.prototype
1498 * @implements {DOMAgent.Dispatcher}
1499 * @param {!WebInspector.DOMModel} domModel
1501 WebInspector.DOMDispatcher = function(domModel)
1503 this._domModel = domModel;
1506 WebInspector.DOMDispatcher.prototype = {
1507 documentUpdated: function()
1509 this._domModel._documentUpdated();
1513 * @param {!DOMAgent.NodeId} nodeId
1515 inspectNodeRequested: function(nodeId)
1517 this._domModel._inspectNodeRequested(nodeId);
1521 * @param {!DOMAgent.NodeId} nodeId
1522 * @param {string} name
1523 * @param {string} value
1525 attributeModified: function(nodeId, name, value)
1527 this._domModel._attributeModified(nodeId, name, value);
1531 * @param {!DOMAgent.NodeId} nodeId
1532 * @param {string} name
1534 attributeRemoved: function(nodeId, name)
1536 this._domModel._attributeRemoved(nodeId, name);
1540 * @param {!Array.<!DOMAgent.NodeId>} nodeIds
1542 inlineStyleInvalidated: function(nodeIds)
1544 this._domModel._inlineStyleInvalidated(nodeIds);
1548 * @param {!DOMAgent.NodeId} nodeId
1549 * @param {string} characterData
1551 characterDataModified: function(nodeId, characterData)
1553 this._domModel._characterDataModified(nodeId, characterData);
1557 * @param {!DOMAgent.NodeId} parentId
1558 * @param {!Array.<!DOMAgent.Node>} payloads
1560 setChildNodes: function(parentId, payloads)
1562 this._domModel._setChildNodes(parentId, payloads);
1566 * @param {!DOMAgent.NodeId} nodeId
1567 * @param {number} childNodeCount
1569 childNodeCountUpdated: function(nodeId, childNodeCount)
1571 this._domModel._childNodeCountUpdated(nodeId, childNodeCount);
1575 * @param {!DOMAgent.NodeId} parentNodeId
1576 * @param {!DOMAgent.NodeId} previousNodeId
1577 * @param {!DOMAgent.Node} payload
1579 childNodeInserted: function(parentNodeId, previousNodeId, payload)
1581 this._domModel._childNodeInserted(parentNodeId, previousNodeId, payload);
1585 * @param {!DOMAgent.NodeId} parentNodeId
1586 * @param {!DOMAgent.NodeId} nodeId
1588 childNodeRemoved: function(parentNodeId, nodeId)
1590 this._domModel._childNodeRemoved(parentNodeId, nodeId);
1594 * @param {!DOMAgent.NodeId} hostId
1595 * @param {!DOMAgent.Node} root
1597 shadowRootPushed: function(hostId, root)
1599 this._domModel._shadowRootPushed(hostId, root);
1603 * @param {!DOMAgent.NodeId} hostId
1604 * @param {!DOMAgent.NodeId} rootId
1606 shadowRootPopped: function(hostId, rootId)
1608 this._domModel._shadowRootPopped(hostId, rootId);
1612 * @param {!DOMAgent.NodeId} parentId
1613 * @param {!DOMAgent.Node} pseudoElement
1615 pseudoElementAdded: function(parentId, pseudoElement)
1617 this._domModel._pseudoElementAdded(parentId, pseudoElement);
1621 * @param {!DOMAgent.NodeId} parentId
1622 * @param {!DOMAgent.NodeId} pseudoElementId
1624 pseudoElementRemoved: function(parentId, pseudoElementId)
1626 this._domModel._pseudoElementRemoved(parentId, pseudoElementId);
1633 WebInspector.DOMNodeHighlighter = function() {
1636 WebInspector.DOMNodeHighlighter.prototype = {
1638 * @param {!DOMAgent.NodeId} nodeId
1639 * @param {!DOMAgent.HighlightConfig} config
1640 * @param {!RuntimeAgent.RemoteObjectId=} objectId
1642 highlightDOMNode: function(nodeId, config, objectId) {},
1645 * @param {boolean} enabled
1646 * @param {boolean} inspectUAShadowDOM
1647 * @param {!DOMAgent.HighlightConfig} config
1648 * @param {function(?Protocol.Error)=} callback
1650 setInspectModeEnabled: function(enabled, inspectUAShadowDOM, config, callback) {}
1655 * @implements {WebInspector.DOMNodeHighlighter}
1657 WebInspector.DefaultDOMNodeHighlighter = function() {
1660 WebInspector.DefaultDOMNodeHighlighter.prototype = {
1662 * @param {!DOMAgent.NodeId} nodeId
1663 * @param {!DOMAgent.HighlightConfig} config
1664 * @param {!RuntimeAgent.RemoteObjectId=} objectId
1666 highlightDOMNode: function(nodeId, config, objectId)
1668 if (objectId || nodeId)
1669 DOMAgent.highlightNode(config, objectId ? undefined : nodeId, objectId);
1671 DOMAgent.hideHighlight();
1675 * @param {boolean} enabled
1676 * @param {boolean} inspectUAShadowDOM
1677 * @param {!DOMAgent.HighlightConfig} config
1678 * @param {function(?Protocol.Error)=} callback
1680 setInspectModeEnabled: function(enabled, inspectUAShadowDOM, config, callback)
1682 DOMAgent.setInspectModeEnabled(enabled, inspectUAShadowDOM, config, callback);
1687 * @type {!WebInspector.DOMModel}
1689 WebInspector.domModel;