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.DOMAgent} domAgent
35 * @param {?WebInspector.DOMDocument} doc
36 * @param {boolean} isInShadowTree
37 * @param {!DOMAgent.Node} payload
39 WebInspector.DOMNode = function(domAgent, doc, isInShadowTree, payload) {
40 this._domAgent = domAgent;
41 this.ownerDocument = doc;
42 this._isInShadowTree = isInShadowTree;
44 this.id = payload.nodeId;
45 domAgent._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;
53 this._shadowRoots = [];
55 this._attributes = [];
56 this._attributesMap = {};
57 if (payload.attributes)
58 this._setAttributesPayload(payload.attributes);
60 this._userProperties = {};
61 this._descendantUserPropertyCounters = {};
63 this._childNodeCount = payload.childNodeCount || 0;
64 this._children = null;
66 this.nextSibling = null;
67 this.previousSibling = null;
68 this.firstChild = null;
69 this.lastChild = null;
70 this.parentNode = null;
72 if (payload.shadowRoots) {
73 for (var i = 0; i < payload.shadowRoots.length; ++i) {
74 var root = payload.shadowRoots[i];
75 var node = new WebInspector.DOMNode(this._domAgent, this.ownerDocument, true, root);
76 this._shadowRoots.push(node);
77 node.parentNode = this;
81 if (payload.templateContent) {
82 this._templateContent = new WebInspector.DOMNode(this._domAgent, this.ownerDocument, true, payload.templateContent);
83 this._templateContent.parentNode = this;
86 if (payload.importedDocument) {
87 this._importedDocument = new WebInspector.DOMNode(this._domAgent, this.ownerDocument, true, payload.importedDocument);
88 this._importedDocument.parentNode = this;
92 this._setChildrenPayload(payload.children);
94 this._setPseudoElements(payload.pseudoElements);
96 if (payload.contentDocument) {
97 this._contentDocument = new WebInspector.DOMDocument(domAgent, payload.contentDocument);
98 this._children = [this._contentDocument];
102 if (this._nodeType === Node.ELEMENT_NODE) {
103 // HTML and BODY from internal iframes should not overwrite top-level ones.
104 if (this.ownerDocument && !this.ownerDocument.documentElement && this._nodeName === "HTML")
105 this.ownerDocument.documentElement = this;
106 if (this.ownerDocument && !this.ownerDocument.body && this._nodeName === "BODY")
107 this.ownerDocument.body = this;
108 } else if (this._nodeType === Node.DOCUMENT_TYPE_NODE) {
109 this.publicId = payload.publicId;
110 this.systemId = payload.systemId;
111 this.internalSubset = payload.internalSubset;
112 } else if (this._nodeType === Node.ATTRIBUTE_NODE) {
113 this.name = payload.name;
114 this.value = payload.value;
118 WebInspector.DOMNode.PseudoElementNames = {
123 WebInspector.DOMNode.ShadowRootTypes = {
124 UserAgent: "user-agent",
128 WebInspector.DOMNode.prototype = {
130 * @return {?Array.<!WebInspector.DOMNode>}
134 return this._children ? this._children.slice() : null;
140 hasAttributes: function()
142 return this._attributes.length > 0;
148 childNodeCount: function()
150 return this._childNodeCount;
156 hasShadowRoots: function()
158 return !!this._shadowRoots.length;
162 * @return {!Array.<!WebInspector.DOMNode>}
164 shadowRoots: function()
166 return this._shadowRoots.slice();
170 * @return {?WebInspector.DOMNode}
172 templateContent: function()
174 return this._templateContent;
178 * @return {?WebInspector.DOMNode}
180 importedDocument: function()
182 return this._importedDocument;
190 return this._nodeType;
198 return this._nodeName;
202 * @return {string|undefined}
204 pseudoType: function()
206 return this._pseudoType;
212 hasPseudoElements: function()
214 return Object.keys(this._pseudoElements).length !== 0;
218 * @return {!Object.<string, !WebInspector.DOMNode>}
220 pseudoElements: function()
222 return this._pseudoElements;
228 isInShadowTree: function()
230 return this._isInShadowTree;
236 shadowRootType: function()
238 return this._shadowRootType || null;
244 nodeNameInCorrectCase: function()
246 return this.isXMLNode() ? this.nodeName() : this.nodeName().toLowerCase();
250 * @param {string} name
251 * @param {function(?Protocol.Error)=} callback
253 setNodeName: function(name, callback)
255 DOMAgent.setNodeName(this.id, name, WebInspector.domAgent._markRevision(this, callback));
261 localName: function()
263 return this._localName;
269 nodeValue: function()
271 return this._nodeValue;
275 * @param {string} value
276 * @param {function(?Protocol.Error)=} callback
278 setNodeValue: function(value, callback)
280 DOMAgent.setNodeValue(this.id, value, WebInspector.domAgent._markRevision(this, callback));
284 * @param {string} name
287 getAttribute: function(name)
289 var attr = this._attributesMap[name];
290 return attr ? attr.value : undefined;
294 * @param {string} name
295 * @param {string} text
296 * @param {function(?Protocol.Error)=} callback
298 setAttribute: function(name, text, callback)
300 DOMAgent.setAttributesAsText(this.id, text, name, WebInspector.domAgent._markRevision(this, callback));
304 * @param {string} name
305 * @param {string} value
306 * @param {function(?Protocol.Error)=} callback
308 setAttributeValue: function(name, value, callback)
310 DOMAgent.setAttributeValue(this.id, name, value, WebInspector.domAgent._markRevision(this, callback));
316 attributes: function()
318 return this._attributes;
322 * @param {string} name
323 * @param {function(?Protocol.Error)=} callback
325 removeAttribute: function(name, callback)
328 * @param {?Protocol.Error} error
329 * @this {WebInspector.DOMNode}
331 function mycallback(error)
334 delete this._attributesMap[name];
335 for (var i = 0; i < this._attributes.length; ++i) {
336 if (this._attributes[i].name === name) {
337 this._attributes.splice(i, 1);
343 WebInspector.domAgent._markRevision(this, callback)(error);
345 DOMAgent.removeAttribute(this.id, name, mycallback.bind(this));
349 * @param {function(?Array.<!WebInspector.DOMNode>)=} callback
351 getChildNodes: function(callback)
353 if (this._children) {
355 callback(this.children());
360 * @this {WebInspector.DOMNode}
361 * @param {?Protocol.Error} error
363 function mycallback(error)
366 callback(error ? null : this.children());
369 DOMAgent.requestChildNodes(this.id, undefined, mycallback.bind(this));
373 * @param {number} depth
374 * @param {function(?Array.<!WebInspector.DOMNode>)=} callback
376 getSubtree: function(depth, callback)
379 * @this {WebInspector.DOMNode}
380 * @param {?Protocol.Error} error
382 function mycallback(error)
385 callback(error ? null : this._children);
388 DOMAgent.requestChildNodes(this.id, depth, mycallback.bind(this));
392 * @param {function(?Protocol.Error)=} callback
394 getOuterHTML: function(callback)
396 DOMAgent.getOuterHTML(this.id, callback);
400 * @param {string} html
401 * @param {function(?Protocol.Error)=} callback
403 setOuterHTML: function(html, callback)
405 DOMAgent.setOuterHTML(this.id, html, WebInspector.domAgent._markRevision(this, callback));
409 * @param {function(?Protocol.Error, !DOMAgent.NodeId=)=} callback
411 removeNode: function(callback)
413 DOMAgent.removeNode(this.id, WebInspector.domAgent._markRevision(this, callback));
418 function copy(error, text)
421 InspectorFrontendHost.copyText(text);
423 DOMAgent.getOuterHTML(this.id, copy);
427 * @param {string} objectGroupId
428 * @param {function(?Protocol.Error)=} callback
430 eventListeners: function(objectGroupId, callback)
432 DOMAgent.getEventListenersForNode(this.id, objectGroupId, callback);
442 while (node && "index" in node && node._nodeName.length) {
443 path.push([node.index, node._nodeName]);
444 node = node.parentNode;
447 return path.join(",");
451 * @param {!WebInspector.DOMNode} node
454 isAncestor: function(node)
459 var currentNode = node.parentNode;
460 while (currentNode) {
461 if (this === currentNode)
463 currentNode = currentNode.parentNode;
469 * @param {!WebInspector.DOMNode} descendant
472 isDescendant: function(descendant)
474 return descendant !== null && descendant.isAncestor(this);
478 * @param {!Array.<string>} attrs
481 _setAttributesPayload: function(attrs)
483 var attributesChanged = !this._attributes || attrs.length !== this._attributes.length * 2;
484 var oldAttributesMap = this._attributesMap || {};
486 this._attributes = [];
487 this._attributesMap = {};
489 for (var i = 0; i < attrs.length; i += 2) {
491 var value = attrs[i + 1];
492 this._addAttribute(name, value);
494 if (attributesChanged)
497 if (!oldAttributesMap[name] || oldAttributesMap[name].value !== value)
498 attributesChanged = true;
500 return attributesChanged;
504 * @param {!WebInspector.DOMNode} prev
505 * @param {!DOMAgent.Node} payload
506 * @return {!WebInspector.DOMNode}
508 _insertChild: function(prev, payload)
510 var node = new WebInspector.DOMNode(this._domAgent, this.ownerDocument, this._isInShadowTree, payload);
511 this._children.splice(this._children.indexOf(prev) + 1, 0, node);
517 * @param {!WebInspector.DOMNode} node
519 _removeChild: function(node)
521 if (node.pseudoType()) {
522 delete this._pseudoElements[node.pseudoType()];
524 var shadowRootIndex = this._shadowRoots.indexOf(node);
525 if (shadowRootIndex !== -1)
526 this._shadowRoots.splice(shadowRootIndex, 1);
528 this._children.splice(this._children.indexOf(node), 1);
530 node.parentNode = null;
531 node._updateChildUserPropertyCountsOnRemoval(this);
536 * @param {!Array.<!DOMAgent.Node>} payloads
538 _setChildrenPayload: function(payloads)
540 // We set children in the constructor.
541 if (this._contentDocument)
545 for (var i = 0; i < payloads.length; ++i) {
546 var payload = payloads[i];
547 var node = new WebInspector.DOMNode(this._domAgent, this.ownerDocument, this._isInShadowTree, payload);
548 this._children.push(node);
554 * @param {!Array.<!DOMAgent.Node>|undefined} payloads
556 _setPseudoElements: function(payloads)
558 this._pseudoElements = {};
562 for (var i = 0; i < payloads.length; ++i) {
563 var node = new WebInspector.DOMNode(this._domAgent, this.ownerDocument, this._isInShadowTree, payloads[i]);
564 node.parentNode = this;
565 this._pseudoElements[node.pseudoType()] = node;
569 _renumber: function()
571 this._childNodeCount = this._children.length;
572 if (this._childNodeCount == 0) {
573 this.firstChild = null;
574 this.lastChild = null;
577 this.firstChild = this._children[0];
578 this.lastChild = this._children[this._childNodeCount - 1];
579 for (var i = 0; i < this._childNodeCount; ++i) {
580 var child = this._children[i];
582 child.nextSibling = i + 1 < this._childNodeCount ? this._children[i + 1] : null;
583 child.previousSibling = i - 1 >= 0 ? this._children[i - 1] : null;
584 child.parentNode = this;
589 * @param {string} name
590 * @param {string} value
592 _addAttribute: function(name, value)
599 this._attributesMap[name] = attr;
600 this._attributes.push(attr);
604 * @param {string} name
605 * @param {string} value
607 _setAttribute: function(name, value)
609 var attr = this._attributesMap[name];
613 this._addAttribute(name, value);
617 * @param {string} name
619 _removeAttribute: function(name)
621 var attr = this._attributesMap[name];
623 this._attributes.remove(attr);
624 delete this._attributesMap[name];
629 * @param {!WebInspector.DOMNode} targetNode
630 * @param {?WebInspector.DOMNode} anchorNode
631 * @param {function(?Protocol.Error, !DOMAgent.NodeId=)=} callback
633 moveTo: function(targetNode, anchorNode, callback)
635 DOMAgent.moveTo(this.id, targetNode.id, anchorNode ? anchorNode.id : undefined, WebInspector.domAgent._markRevision(this, callback));
641 isXMLNode: function()
643 return !!this.ownerDocument && !!this.ownerDocument.xmlVersion;
646 _updateChildUserPropertyCountsOnRemoval: function(parentNode)
649 if (this._userProperties) {
650 for (var name in this._userProperties)
651 result[name] = (result[name] || 0) + 1;
654 if (this._descendantUserPropertyCounters) {
655 for (var name in this._descendantUserPropertyCounters) {
656 var counter = this._descendantUserPropertyCounters[name];
657 result[name] = (result[name] || 0) + counter;
661 for (var name in result)
662 parentNode._updateDescendantUserPropertyCount(name, -result[name]);
665 _updateDescendantUserPropertyCount: function(name, delta)
667 if (!this._descendantUserPropertyCounters.hasOwnProperty(name))
668 this._descendantUserPropertyCounters[name] = 0;
669 this._descendantUserPropertyCounters[name] += delta;
670 if (!this._descendantUserPropertyCounters[name])
671 delete this._descendantUserPropertyCounters[name];
673 this.parentNode._updateDescendantUserPropertyCount(name, delta);
676 setUserProperty: function(name, value)
678 if (value === null) {
679 this.removeUserProperty(name);
683 if (this.parentNode && !this._userProperties.hasOwnProperty(name))
684 this.parentNode._updateDescendantUserPropertyCount(name, 1);
686 this._userProperties[name] = value;
689 removeUserProperty: function(name)
691 if (!this._userProperties.hasOwnProperty(name))
694 delete this._userProperties[name];
696 this.parentNode._updateDescendantUserPropertyCount(name, -1);
700 * @param {string} name
704 getUserProperty: function(name)
706 return (this._userProperties && this._userProperties[name]) || null;
710 * @param {string} name
713 descendantUserPropertyCount: function(name)
715 return this._descendantUserPropertyCounters && this._descendantUserPropertyCounters[name] ? this._descendantUserPropertyCounters[name] : 0;
719 * @param {string} url
722 resolveURL: function(url)
726 for (var frameOwnerCandidate = this; frameOwnerCandidate; frameOwnerCandidate = frameOwnerCandidate.parentNode) {
727 if (frameOwnerCandidate.baseURL)
728 return WebInspector.ParsedURL.completeURL(frameOwnerCandidate.baseURL, url);
735 * @extends {WebInspector.DOMNode}
737 * @param {!WebInspector.DOMAgent} domAgent
738 * @param {!DOMAgent.Node} payload
740 WebInspector.DOMDocument = function(domAgent, payload)
742 WebInspector.DOMNode.call(this, domAgent, this, false, payload);
743 this.documentURL = payload.documentURL || "";
744 this.baseURL = payload.baseURL || "";
745 this.xmlVersion = payload.xmlVersion;
746 this._listeners = {};
749 WebInspector.DOMDocument.prototype = {
750 __proto__: WebInspector.DOMNode.prototype
754 * @extends {WebInspector.Object}
757 WebInspector.DOMAgent = function() {
758 /** @type {!Object.<number, !WebInspector.DOMNode>} */
759 this._idToDOMNode = {};
760 /** @type {?WebInspector.DOMDocument} */
761 this._document = null;
762 /** @type {!Object.<number, boolean>} */
763 this._attributeLoadNodeIds = {};
764 InspectorBackend.registerDOMDispatcher(new WebInspector.DOMDispatcher(this));
766 this._defaultHighlighter = new WebInspector.DefaultDOMNodeHighlighter();
767 this._highlighter = this._defaultHighlighter;
770 WebInspector.DOMAgent.Events = {
771 AttrModified: "AttrModified",
772 AttrRemoved: "AttrRemoved",
773 CharacterDataModified: "CharacterDataModified",
774 NodeInserted: "NodeInserted",
775 NodeRemoved: "NodeRemoved",
776 DocumentUpdated: "DocumentUpdated",
777 ChildNodeCountUpdated: "ChildNodeCountUpdated",
778 UndoRedoRequested: "UndoRedoRequested",
779 UndoRedoCompleted: "UndoRedoCompleted",
780 InspectNodeRequested: "InspectNodeRequested"
783 WebInspector.DOMAgent.prototype = {
785 * @param {function(!WebInspector.DOMDocument)=} callback
787 requestDocument: function(callback)
789 if (this._document) {
791 callback(this._document);
795 if (this._pendingDocumentRequestCallbacks) {
796 this._pendingDocumentRequestCallbacks.push(callback);
800 this._pendingDocumentRequestCallbacks = [callback];
803 * @this {WebInspector.DOMAgent}
804 * @param {?Protocol.Error} error
805 * @param {!DOMAgent.Node} root
807 function onDocumentAvailable(error, root)
810 this._setDocument(root);
812 for (var i = 0; i < this._pendingDocumentRequestCallbacks.length; ++i) {
813 var callback = this._pendingDocumentRequestCallbacks[i];
815 callback(this._document);
817 delete this._pendingDocumentRequestCallbacks;
820 DOMAgent.getDocument(onDocumentAvailable.bind(this));
824 * @return {?WebInspector.DOMDocument}
826 existingDocument: function()
828 return this._document;
832 * @param {!RuntimeAgent.RemoteObjectId} objectId
833 * @param {function(?DOMAgent.NodeId)=} callback
835 pushNodeToFrontend: function(objectId, callback)
837 this._dispatchWhenDocumentAvailable(DOMAgent.requestNode.bind(DOMAgent, objectId), callback);
841 * @param {string} path
842 * @param {function(?number)=} callback
844 pushNodeByPathToFrontend: function(path, callback)
846 this._dispatchWhenDocumentAvailable(DOMAgent.pushNodeByPathToFrontend.bind(DOMAgent, path), callback);
850 * @param {number} backendNodeId
851 * @param {function(?number)=} callback
853 pushNodeByBackendIdToFrontend: function(backendNodeId, callback)
855 this._dispatchWhenDocumentAvailable(DOMAgent.pushNodeByBackendIdToFrontend.bind(DOMAgent, backendNodeId), callback);
859 * @param {function(!T)=} callback
860 * @return {function(?Protocol.Error, !T=)|undefined}
863 _wrapClientCallback: function(callback)
868 * @param {?Protocol.Error} error
869 * @param {!T=} result
872 return function(error, result)
874 // Caller is responsible for handling the actual error.
875 callback(error ? null : result);
880 * @param {function(function(?Protocol.Error, !T=)=)} func
881 * @param {function(!T)=} callback
884 _dispatchWhenDocumentAvailable: function(func, callback)
886 var callbackWrapper = this._wrapClientCallback(callback);
889 * @this {WebInspector.DOMAgent}
891 function onDocumentAvailable()
894 func(callbackWrapper);
897 callbackWrapper("No document");
900 this.requestDocument(onDocumentAvailable.bind(this));
904 * @param {!DOMAgent.NodeId} nodeId
905 * @param {string} name
906 * @param {string} value
908 _attributeModified: function(nodeId, name, value)
910 var node = this._idToDOMNode[nodeId];
914 node._setAttribute(name, value);
915 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.AttrModified, { node: node, name: name });
919 * @param {!DOMAgent.NodeId} nodeId
920 * @param {string} name
922 _attributeRemoved: function(nodeId, name)
924 var node = this._idToDOMNode[nodeId];
927 node._removeAttribute(name);
928 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.AttrRemoved, { node: node, name: name });
932 * @param {!Array.<!DOMAgent.NodeId>} nodeIds
934 _inlineStyleInvalidated: function(nodeIds)
936 for (var i = 0; i < nodeIds.length; ++i)
937 this._attributeLoadNodeIds[nodeIds[i]] = true;
938 if ("_loadNodeAttributesTimeout" in this)
940 this._loadNodeAttributesTimeout = setTimeout(this._loadNodeAttributes.bind(this), 20);
943 _loadNodeAttributes: function()
946 * @this {WebInspector.DOMAgent}
947 * @param {!DOMAgent.NodeId} nodeId
948 * @param {?Protocol.Error} error
949 * @param {!Array.<string>} attributes
951 function callback(nodeId, error, attributes)
954 // We are calling _loadNodeAttributes asynchronously, it is ok if node is not found.
957 var node = this._idToDOMNode[nodeId];
959 if (node._setAttributesPayload(attributes))
960 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.AttrModified, { node: node, name: "style" });
964 delete this._loadNodeAttributesTimeout;
966 for (var nodeId in this._attributeLoadNodeIds) {
967 var nodeIdAsNumber = parseInt(nodeId, 10);
968 DOMAgent.getAttributes(nodeIdAsNumber, callback.bind(this, nodeIdAsNumber));
970 this._attributeLoadNodeIds = {};
974 * @param {!DOMAgent.NodeId} nodeId
975 * @param {string} newValue
977 _characterDataModified: function(nodeId, newValue)
979 var node = this._idToDOMNode[nodeId];
980 node._nodeValue = newValue;
981 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.CharacterDataModified, node);
985 * @param {!DOMAgent.NodeId} nodeId
986 * @return {?WebInspector.DOMNode}
988 nodeForId: function(nodeId)
990 return this._idToDOMNode[nodeId] || null;
993 _documentUpdated: function()
995 this._setDocument(null);
999 * @param {?DOMAgent.Node} payload
1001 _setDocument: function(payload)
1003 this._idToDOMNode = {};
1004 if (payload && "nodeId" in payload)
1005 this._document = new WebInspector.DOMDocument(this, payload);
1007 this._document = null;
1008 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.DocumentUpdated, this._document);
1012 * @param {!DOMAgent.Node} payload
1014 _setDetachedRoot: function(payload)
1016 if (payload.nodeName === "#document")
1017 new WebInspector.DOMDocument(this, payload);
1019 new WebInspector.DOMNode(this, null, false, payload);
1023 * @param {!DOMAgent.NodeId} parentId
1024 * @param {!Array.<!DOMAgent.Node>} payloads
1026 _setChildNodes: function(parentId, payloads)
1028 if (!parentId && payloads.length) {
1029 this._setDetachedRoot(payloads[0]);
1033 var parent = this._idToDOMNode[parentId];
1034 parent._setChildrenPayload(payloads);
1038 * @param {!DOMAgent.NodeId} nodeId
1039 * @param {number} newValue
1041 _childNodeCountUpdated: function(nodeId, newValue)
1043 var node = this._idToDOMNode[nodeId];
1044 node._childNodeCount = newValue;
1045 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.ChildNodeCountUpdated, node);
1049 * @param {!DOMAgent.NodeId} parentId
1050 * @param {!DOMAgent.NodeId} prevId
1051 * @param {!DOMAgent.Node} payload
1053 _childNodeInserted: function(parentId, prevId, payload)
1055 var parent = this._idToDOMNode[parentId];
1056 var prev = this._idToDOMNode[prevId];
1057 var node = parent._insertChild(prev, payload);
1058 this._idToDOMNode[node.id] = node;
1059 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.NodeInserted, node);
1063 * @param {!DOMAgent.NodeId} parentId
1064 * @param {!DOMAgent.NodeId} nodeId
1066 _childNodeRemoved: function(parentId, nodeId)
1068 var parent = this._idToDOMNode[parentId];
1069 var node = this._idToDOMNode[nodeId];
1070 parent._removeChild(node);
1072 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.NodeRemoved, {node: node, parent: parent});
1076 * @param {!DOMAgent.NodeId} hostId
1077 * @param {!DOMAgent.Node} root
1079 _shadowRootPushed: function(hostId, root)
1081 var host = this._idToDOMNode[hostId];
1084 var node = new WebInspector.DOMNode(this, host.ownerDocument, true, root);
1085 node.parentNode = host;
1086 this._idToDOMNode[node.id] = node;
1087 host._shadowRoots.push(node);
1088 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.NodeInserted, node);
1092 * @param {!DOMAgent.NodeId} hostId
1093 * @param {!DOMAgent.NodeId} rootId
1095 _shadowRootPopped: function(hostId, rootId)
1097 var host = this._idToDOMNode[hostId];
1100 var root = this._idToDOMNode[rootId];
1103 host._removeChild(root);
1105 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.NodeRemoved, {node: root, parent: host});
1109 * @param {!DOMAgent.NodeId} parentId
1110 * @param {!DOMAgent.Node} pseudoElement
1112 _pseudoElementAdded: function(parentId, pseudoElement)
1114 var parent = this._idToDOMNode[parentId];
1117 var node = new WebInspector.DOMNode(this, parent.ownerDocument, false, pseudoElement);
1118 node.parentNode = parent;
1119 this._idToDOMNode[node.id] = node;
1120 console.assert(!parent._pseudoElements[node.pseudoType()]);
1121 parent._pseudoElements[node.pseudoType()] = node;
1122 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.NodeInserted, node);
1126 * @param {!DOMAgent.NodeId} parentId
1127 * @param {!DOMAgent.NodeId} pseudoElementId
1129 _pseudoElementRemoved: function(parentId, pseudoElementId)
1131 var parent = this._idToDOMNode[parentId];
1134 var pseudoElement = this._idToDOMNode[pseudoElementId];
1137 parent._removeChild(pseudoElement);
1138 this._unbind(pseudoElement);
1139 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.NodeRemoved, {node: pseudoElement, parent: parent});
1143 * @param {!WebInspector.DOMNode} node
1145 _unbind: function(node)
1147 delete this._idToDOMNode[node.id];
1148 for (var i = 0; node._children && i < node._children.length; ++i)
1149 this._unbind(node._children[i]);
1150 for (var i = 0; i < node._shadowRoots.length; ++i)
1151 this._unbind(node._shadowRoots[i]);
1152 var pseudoElements = node.pseudoElements();
1153 for (var id in pseudoElements)
1154 this._unbind(pseudoElements[id]);
1155 if (node._templateContent)
1156 this._unbind(node._templateContent);
1160 * @param {number} nodeId
1162 inspectElement: function(nodeId)
1164 var node = this._idToDOMNode[nodeId];
1166 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.InspectNodeRequested, nodeId);
1170 * @param {!DOMAgent.NodeId} nodeId
1172 _inspectNodeRequested: function(nodeId)
1174 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.InspectNodeRequested, nodeId);
1178 * @param {string} query
1179 * @param {function(number)} searchCallback
1181 performSearch: function(query, searchCallback)
1183 this.cancelSearch();
1186 * @param {?Protocol.Error} error
1187 * @param {string} searchId
1188 * @param {number} resultsCount
1189 * @this {WebInspector.DOMAgent}
1191 function callback(error, searchId, resultsCount)
1193 this._searchId = searchId;
1194 searchCallback(resultsCount);
1196 DOMAgent.performSearch(query, callback.bind(this));
1200 * @param {number} index
1201 * @param {?function(?WebInspector.DOMNode)} callback
1203 searchResult: function(index, callback)
1206 DOMAgent.getSearchResults(this._searchId, index, index + 1, searchResultsCallback.bind(this));
1211 * @param {?Protocol.Error} error
1212 * @param {!Array.<number>} nodeIds
1213 * @this {WebInspector.DOMAgent}
1215 function searchResultsCallback(error, nodeIds)
1218 console.error(error);
1222 if (nodeIds.length != 1)
1225 callback(this.nodeForId(nodeIds[0]));
1229 cancelSearch: function()
1231 if (this._searchId) {
1232 DOMAgent.discardSearchResults(this._searchId);
1233 delete this._searchId;
1238 * @param {!DOMAgent.NodeId} nodeId
1239 * @param {string} selectors
1240 * @param {function(?DOMAgent.NodeId)=} callback
1242 querySelector: function(nodeId, selectors, callback)
1244 DOMAgent.querySelector(nodeId, selectors, this._wrapClientCallback(callback));
1248 * @param {!DOMAgent.NodeId} nodeId
1249 * @param {string} selectors
1250 * @param {function(!Array.<!DOMAgent.NodeId>=)=} callback
1252 querySelectorAll: function(nodeId, selectors, callback)
1254 DOMAgent.querySelectorAll(nodeId, selectors, this._wrapClientCallback(callback));
1258 * @param {!DOMAgent.NodeId=} nodeId
1259 * @param {string=} mode
1260 * @param {!RuntimeAgent.RemoteObjectId=} objectId
1262 highlightDOMNode: function(nodeId, mode, objectId)
1264 if (this._hideDOMNodeHighlightTimeout) {
1265 clearTimeout(this._hideDOMNodeHighlightTimeout);
1266 delete this._hideDOMNodeHighlightTimeout;
1268 this._highlighter.highlightDOMNode(nodeId || 0, this._buildHighlightConfig(mode), objectId);
1271 hideDOMNodeHighlight: function()
1273 this.highlightDOMNode(0);
1277 * @param {!DOMAgent.NodeId} nodeId
1279 highlightDOMNodeForTwoSeconds: function(nodeId)
1281 this.highlightDOMNode(nodeId);
1282 this._hideDOMNodeHighlightTimeout = setTimeout(this.hideDOMNodeHighlight.bind(this), 2000);
1286 * @param {boolean} enabled
1287 * @param {boolean} inspectShadowDOM
1288 * @param {function(?Protocol.Error)=} callback
1290 setInspectModeEnabled: function(enabled, inspectShadowDOM, callback)
1293 * @this {WebInspector.DOMAgent}
1295 function onDocumentAvailable()
1297 this._highlighter.setInspectModeEnabled(enabled, inspectShadowDOM, this._buildHighlightConfig(), callback);
1299 this.requestDocument(onDocumentAvailable.bind(this));
1303 * @param {string=} mode
1304 * @return {!DOMAgent.HighlightConfig}
1306 _buildHighlightConfig: function(mode)
1308 mode = mode || "all";
1309 var highlightConfig = { showInfo: mode === "all", showRulers: WebInspector.settings.showMetricsRulers.get() };
1310 if (mode === "all" || mode === "content")
1311 highlightConfig.contentColor = WebInspector.Color.PageHighlight.Content.toProtocolRGBA();
1313 if (mode === "all" || mode === "padding")
1314 highlightConfig.paddingColor = WebInspector.Color.PageHighlight.Padding.toProtocolRGBA();
1316 if (mode === "all" || mode === "border")
1317 highlightConfig.borderColor = WebInspector.Color.PageHighlight.Border.toProtocolRGBA();
1319 if (mode === "all" || mode === "margin")
1320 highlightConfig.marginColor = WebInspector.Color.PageHighlight.Margin.toProtocolRGBA();
1323 highlightConfig.eventTargetColor = WebInspector.Color.PageHighlight.EventTarget.toProtocolRGBA();
1325 return highlightConfig;
1329 * @param {!WebInspector.DOMNode} node
1330 * @param {function(?Protocol.Error, !A=, !B=)=} callback
1331 * @return {function(?Protocol.Error, !A=, !B=)}
1334 _markRevision: function(node, callback)
1337 * @param {?Protocol.Error} error
1338 * @this {WebInspector.DOMAgent}
1340 function wrapperFunction(error)
1343 this.markUndoableState();
1346 callback.apply(this, arguments);
1348 return wrapperFunction.bind(this);
1352 * @param {boolean} emulationEnabled
1354 emulateTouchEventObjects: function(emulationEnabled)
1356 const injectedFunction = function() {
1357 const touchEvents = ["ontouchstart", "ontouchend", "ontouchmove", "ontouchcancel"];
1358 var recepients = [window.__proto__, document.__proto__];
1359 for (var i = 0; i < touchEvents.length; ++i) {
1360 for (var j = 0; j < recepients.length; ++j) {
1361 if (!(touchEvents[i] in recepients[j]))
1362 Object.defineProperty(recepients[j], touchEvents[i], { value: null, writable: true, configurable: true, enumerable: true });
1367 if (emulationEnabled && !this._addTouchEventsScriptInjecting) {
1368 this._addTouchEventsScriptInjecting = true;
1369 PageAgent.addScriptToEvaluateOnLoad("(" + injectedFunction.toString() + ")()", scriptAddedCallback.bind(this));
1371 if (typeof this._addTouchEventsScriptId !== "undefined") {
1372 PageAgent.removeScriptToEvaluateOnLoad(this._addTouchEventsScriptId);
1373 delete this._addTouchEventsScriptId;
1378 * @param {?Protocol.Error} error
1379 * @param {string} scriptId
1380 * @this {WebInspector.DOMAgent}
1382 function scriptAddedCallback(error, scriptId)
1384 delete this._addTouchEventsScriptInjecting;
1387 this._addTouchEventsScriptId = scriptId;
1390 PageAgent.setTouchEmulationEnabled(emulationEnabled);
1393 markUndoableState: function()
1395 DOMAgent.markUndoableState();
1399 * @param {function(?Protocol.Error)=} callback
1401 undo: function(callback)
1404 * @param {?Protocol.Error} error
1405 * @this {WebInspector.DOMAgent}
1407 function mycallback(error)
1409 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.UndoRedoCompleted);
1413 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.UndoRedoRequested);
1414 DOMAgent.undo(callback);
1418 * @param {function(?Protocol.Error)=} callback
1420 redo: function(callback)
1423 * @param {?Protocol.Error} error
1424 * @this {WebInspector.DOMAgent}
1426 function mycallback(error)
1428 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.UndoRedoCompleted);
1432 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.UndoRedoRequested);
1433 DOMAgent.redo(callback);
1437 * @param {?WebInspector.DOMNodeHighlighter} highlighter
1439 setHighlighter: function(highlighter)
1441 this._highlighter = highlighter || this._defaultHighlighter;
1444 __proto__: WebInspector.Object.prototype
1449 * @implements {DOMAgent.Dispatcher}
1450 * @param {!WebInspector.DOMAgent} domAgent
1452 WebInspector.DOMDispatcher = function(domAgent)
1454 this._domAgent = domAgent;
1457 WebInspector.DOMDispatcher.prototype = {
1458 documentUpdated: function()
1460 this._domAgent._documentUpdated();
1464 * @param {!DOMAgent.NodeId} nodeId
1466 inspectNodeRequested: function(nodeId)
1468 this._domAgent._inspectNodeRequested(nodeId);
1472 * @param {!DOMAgent.NodeId} nodeId
1473 * @param {string} name
1474 * @param {string} value
1476 attributeModified: function(nodeId, name, value)
1478 this._domAgent._attributeModified(nodeId, name, value);
1482 * @param {!DOMAgent.NodeId} nodeId
1483 * @param {string} name
1485 attributeRemoved: function(nodeId, name)
1487 this._domAgent._attributeRemoved(nodeId, name);
1491 * @param {!Array.<!DOMAgent.NodeId>} nodeIds
1493 inlineStyleInvalidated: function(nodeIds)
1495 this._domAgent._inlineStyleInvalidated(nodeIds);
1499 * @param {!DOMAgent.NodeId} nodeId
1500 * @param {string} characterData
1502 characterDataModified: function(nodeId, characterData)
1504 this._domAgent._characterDataModified(nodeId, characterData);
1508 * @param {!DOMAgent.NodeId} parentId
1509 * @param {!Array.<!DOMAgent.Node>} payloads
1511 setChildNodes: function(parentId, payloads)
1513 this._domAgent._setChildNodes(parentId, payloads);
1517 * @param {!DOMAgent.NodeId} nodeId
1518 * @param {number} childNodeCount
1520 childNodeCountUpdated: function(nodeId, childNodeCount)
1522 this._domAgent._childNodeCountUpdated(nodeId, childNodeCount);
1526 * @param {!DOMAgent.NodeId} parentNodeId
1527 * @param {!DOMAgent.NodeId} previousNodeId
1528 * @param {!DOMAgent.Node} payload
1530 childNodeInserted: function(parentNodeId, previousNodeId, payload)
1532 this._domAgent._childNodeInserted(parentNodeId, previousNodeId, payload);
1536 * @param {!DOMAgent.NodeId} parentNodeId
1537 * @param {!DOMAgent.NodeId} nodeId
1539 childNodeRemoved: function(parentNodeId, nodeId)
1541 this._domAgent._childNodeRemoved(parentNodeId, nodeId);
1545 * @param {!DOMAgent.NodeId} hostId
1546 * @param {!DOMAgent.Node} root
1548 shadowRootPushed: function(hostId, root)
1550 this._domAgent._shadowRootPushed(hostId, root);
1554 * @param {!DOMAgent.NodeId} hostId
1555 * @param {!DOMAgent.NodeId} rootId
1557 shadowRootPopped: function(hostId, rootId)
1559 this._domAgent._shadowRootPopped(hostId, rootId);
1563 * @param {!DOMAgent.NodeId} parentId
1564 * @param {!DOMAgent.Node} pseudoElement
1566 pseudoElementAdded: function(parentId, pseudoElement)
1568 this._domAgent._pseudoElementAdded(parentId, pseudoElement);
1572 * @param {!DOMAgent.NodeId} parentId
1573 * @param {!DOMAgent.NodeId} pseudoElementId
1575 pseudoElementRemoved: function(parentId, pseudoElementId)
1577 this._domAgent._pseudoElementRemoved(parentId, pseudoElementId);
1584 WebInspector.DOMNodeHighlighter = function() {
1587 WebInspector.DOMNodeHighlighter.prototype = {
1589 * @param {!DOMAgent.NodeId} nodeId
1590 * @param {!DOMAgent.HighlightConfig} config
1591 * @param {!RuntimeAgent.RemoteObjectId=} objectId
1593 highlightDOMNode: function(nodeId, config, objectId) {},
1596 * @param {boolean} enabled
1597 * @param {boolean} inspectShadowDOM
1598 * @param {!DOMAgent.HighlightConfig} config
1599 * @param {function(?Protocol.Error)=} callback
1601 setInspectModeEnabled: function(enabled, inspectShadowDOM, config, callback) {}
1606 * @implements {WebInspector.DOMNodeHighlighter}
1608 WebInspector.DefaultDOMNodeHighlighter = function() {
1611 WebInspector.DefaultDOMNodeHighlighter.prototype = {
1613 * @param {!DOMAgent.NodeId} nodeId
1614 * @param {!DOMAgent.HighlightConfig} config
1615 * @param {!RuntimeAgent.RemoteObjectId=} objectId
1617 highlightDOMNode: function(nodeId, config, objectId)
1619 if (objectId || nodeId)
1620 DOMAgent.highlightNode(config, objectId ? undefined : nodeId, objectId);
1622 DOMAgent.hideHighlight();
1626 * @param {boolean} enabled
1627 * @param {boolean} inspectShadowDOM
1628 * @param {!DOMAgent.HighlightConfig} config
1629 * @param {function(?Protocol.Error)=} callback
1631 setInspectModeEnabled: function(enabled, inspectShadowDOM, config, callback)
1633 DOMAgent.setInspectModeEnabled(enabled, inspectShadowDOM, config, callback);
1638 * @type {!WebInspector.DOMAgent}
1640 WebInspector.domAgent;