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 {DOMAgent.Node} payload
38 WebInspector.DOMNode = function(domAgent, doc, payload) {
39 this._domAgent = domAgent;
40 this.ownerDocument = doc;
42 this.id = payload.nodeId;
43 domAgent._idToDOMNode[this.id] = this;
44 this._nodeType = payload.nodeType;
45 this._nodeName = payload.nodeName;
46 this._localName = payload.localName;
47 this._nodeValue = payload.nodeValue;
49 this._attributes = [];
50 this._attributesMap = {};
51 if (payload.attributes)
52 this._setAttributesPayload(payload.attributes);
54 this._childNodeCount = payload.childNodeCount;
57 this.nextSibling = null;
58 this.previousSibling = null;
59 this.firstChild = null;
60 this.lastChild = null;
61 this.parentNode = null;
64 this._setChildrenPayload(payload.children);
66 if (payload.contentDocument) {
67 this._contentDocument = new WebInspector.DOMDocument(domAgent, payload.contentDocument);
68 this.children = [this._contentDocument];
72 if (this._nodeType === Node.ELEMENT_NODE) {
73 // HTML and BODY from internal iframes should not overwrite top-level ones.
74 if (this.ownerDocument && !this.ownerDocument.documentElement && this._nodeName === "HTML")
75 this.ownerDocument.documentElement = this;
76 if (this.ownerDocument && !this.ownerDocument.body && this._nodeName === "BODY")
77 this.ownerDocument.body = this;
78 } else if (this._nodeType === Node.DOCUMENT_TYPE_NODE) {
79 this.publicId = payload.publicId;
80 this.systemId = payload.systemId;
81 this.internalSubset = payload.internalSubset;
82 } else if (this._nodeType === Node.ATTRIBUTE_NODE) {
83 this.name = payload.name;
84 this.value = payload.value;
90 * @param {string} value
91 * @param {boolean} optimized
93 WebInspector.DOMNode.XPathStep = function(value, optimized)
96 this.optimized = optimized;
99 WebInspector.DOMNode.XPathStep.prototype = {
106 WebInspector.DOMNode.prototype = {
110 hasAttributes: function()
112 return this._attributes.length > 0;
118 hasChildNodes: function()
120 return this._childNodeCount > 0;
128 return this._nodeType;
136 return this._nodeName;
142 nodeNameInCorrectCase: function()
144 return this.isXMLNode() ? this.nodeName() : this.nodeName().toLowerCase();
148 * @param {string} name
149 * @param {function(?Protocol.Error)=} callback
151 setNodeName: function(name, callback)
153 DOMAgent.setNodeName(this.id, name, WebInspector.domAgent._markRevision(this, callback));
159 localName: function()
161 return this._localName;
167 nodeValue: function()
169 return this._nodeValue;
173 * @param {string} value
174 * @param {function(?Protocol.Error)=} callback
176 setNodeValue: function(value, callback)
178 DOMAgent.setNodeValue(this.id, value, WebInspector.domAgent._markRevision(this, callback));
182 * @param {string} name
185 getAttribute: function(name)
187 var attr = this._attributesMap[name];
188 return attr ? attr.value : undefined;
192 * @param {string} name
193 * @param {string} text
194 * @param {function(?Protocol.Error)=} callback
196 setAttribute: function(name, text, callback)
198 DOMAgent.setAttributesAsText(this.id, text, name, WebInspector.domAgent._markRevision(this, callback));
202 * @param {string} name
203 * @param {string} value
204 * @param {function(?Protocol.Error)=} callback
206 setAttributeValue: function(name, value, callback)
208 DOMAgent.setAttributeValue(this.id, name, value, WebInspector.domAgent._markRevision(this, callback));
214 attributes: function()
216 return this._attributes;
220 * @param {string} name
221 * @param {function(?Protocol.Error)=} callback
223 removeAttribute: function(name, callback)
225 function mycallback(error, success)
228 delete this._attributesMap[name];
229 for (var i = 0; i < this._attributes.length; ++i) {
230 if (this._attributes[i].name === name) {
231 this._attributes.splice(i, 1);
237 WebInspector.domAgent._markRevision(this, callback)(error);
239 DOMAgent.removeAttribute(this.id, name, mycallback.bind(this));
243 * @param {function(Array.<WebInspector.DOMNode>)=} callback
245 getChildNodes: function(callback)
249 callback(this.children);
254 * @this {WebInspector.DOMNode}
255 * @param {?Protocol.Error} error
257 function mycallback(error)
259 if (!error && callback)
260 callback(this.children);
263 DOMAgent.requestChildNodes(this.id, mycallback.bind(this));
267 * @param {function(?Protocol.Error)=} callback
269 getOuterHTML: function(callback)
271 DOMAgent.getOuterHTML(this.id, callback);
275 * @param {string} html
276 * @param {function(?Protocol.Error)=} callback
278 setOuterHTML: function(html, callback)
280 DOMAgent.setOuterHTML(this.id, html, WebInspector.domAgent._markRevision(this, callback));
284 * @param {function(?Protocol.Error)=} callback
286 removeNode: function(callback)
288 DOMAgent.removeNode(this.id, WebInspector.domAgent._markRevision(this, callback));
293 function copy(error, text)
296 InspectorFrontendHost.copyText(text);
298 DOMAgent.getOuterHTML(this.id, copy);
302 * @param {boolean} optimized
304 copyXPath: function(optimized)
306 InspectorFrontendHost.copyText(this.xPath(optimized));
310 * @param {function(?Protocol.Error)=} callback
312 eventListeners: function(callback)
314 DOMAgent.getEventListenersForNode(this.id, callback);
324 while (node && "index" in node && node._nodeName.length) {
325 path.push([node.index, node._nodeName]);
326 node = node.parentNode;
329 return path.join(",");
333 * @param {boolean} justSelector
336 appropriateSelectorFor: function(justSelector)
338 var lowerCaseName = this.localName() || this.nodeName().toLowerCase();
340 var id = this.getAttribute("id");
342 var selector = "#" + id;
343 return (justSelector ? selector : lowerCaseName + selector);
346 var className = this.getAttribute("class");
348 var selector = "." + className.replace(/\s+/, ".");
349 return (justSelector ? selector : lowerCaseName + selector);
352 if (lowerCaseName === "input" && this.getAttribute("type"))
353 return lowerCaseName + "[type=\"" + this.getAttribute("type") + "\"]";
355 return lowerCaseName;
359 * @param {WebInspector.DOMNode} node
362 isAncestor: function(node)
367 var currentNode = node.parentNode;
368 while (currentNode) {
369 if (this === currentNode)
371 currentNode = currentNode.parentNode;
377 * @param {WebInspector.DOMNode} descendant
380 isDescendant: function(descendant)
382 return descendant !== null && descendant.isAncestor(this);
386 * @param {Array.<string>} attrs
388 _setAttributesPayload: function(attrs)
390 this._attributes = [];
391 this._attributesMap = {};
392 for (var i = 0; i < attrs.length; i += 2)
393 this._addAttribute(attrs[i], attrs[i + 1]);
397 * @param {WebInspector.DOMNode} prev
398 * @param {DOMAgent.Node} payload
399 * @return {WebInspector.DOMNode}
401 _insertChild: function(prev, payload)
403 var node = new WebInspector.DOMNode(this._domAgent, this.ownerDocument, payload);
405 if (!this.children) {
407 this.children = [ node ];
409 this.children.unshift(node);
411 this.children.splice(this.children.indexOf(prev) + 1, 0, node);
417 * @param {WebInspector.DOMNode} node
419 _removeChild: function(node)
421 this.children.splice(this.children.indexOf(node), 1);
422 node.parentNode = null;
427 * @param {Array.<DOMAgent.Node>} payloads
429 _setChildrenPayload: function(payloads)
431 // We set children in the constructor.
432 if (this._contentDocument)
436 for (var i = 0; i < payloads.length; ++i) {
437 var payload = payloads[i];
438 var node = new WebInspector.DOMNode(this._domAgent, this.ownerDocument, payload);
439 this.children.push(node);
444 _renumber: function()
446 this._childNodeCount = this.children.length;
447 if (this._childNodeCount == 0) {
448 this.firstChild = null;
449 this.lastChild = null;
452 this.firstChild = this.children[0];
453 this.lastChild = this.children[this._childNodeCount - 1];
454 for (var i = 0; i < this._childNodeCount; ++i) {
455 var child = this.children[i];
457 child.nextSibling = i + 1 < this._childNodeCount ? this.children[i + 1] : null;
458 child.previousSibling = i - 1 >= 0 ? this.children[i - 1] : null;
459 child.parentNode = this;
464 * @param {string} name
465 * @param {string} value
467 _addAttribute: function(name, value)
474 this._attributesMap[name] = attr;
475 this._attributes.push(attr);
479 * @param {string} name
480 * @param {string} value
482 _setAttribute: function(name, value)
484 var attr = this._attributesMap[name];
488 this._addAttribute(name, value);
492 * @param {string} name
494 _removeAttribute: function(name)
496 var attr = this._attributesMap[name];
498 this._attributes.remove(attr);
499 delete this._attributesMap[name];
504 * @param {WebInspector.DOMNode} targetNode
505 * @param {?WebInspector.DOMNode} anchorNode
506 * @param {function(?Protocol.Error)=} callback
508 moveTo: function(targetNode, anchorNode, callback)
510 DOMAgent.moveTo(this.id, targetNode.id, anchorNode ? anchorNode.id : undefined, WebInspector.domAgent._markRevision(this, callback));
516 isXMLNode: function()
518 return !!this.ownerDocument && !!this.ownerDocument.xmlVersion;
522 * @param {boolean} optimized
525 xPath: function(optimized)
527 if (this._nodeType === Node.DOCUMENT_NODE)
531 var contextNode = this;
532 while (contextNode) {
533 var step = contextNode._xPathValue(optimized);
535 break; // Error - bail out early.
539 contextNode = contextNode.parentNode;
543 return (steps.length && steps[0].optimized ? "" : "/") + steps.join("/");
547 * @param {boolean} optimized
548 * @return {WebInspector.DOMNode.XPathStep}
550 _xPathValue: function(optimized)
553 var ownIndex = this._xPathIndex();
555 return null; // Error.
557 switch (this._nodeType) {
558 case Node.ELEMENT_NODE:
559 if (optimized && this.getAttribute("id"))
560 return new WebInspector.DOMNode.XPathStep("//*[@id=\"" + this.getAttribute("id") + "\"]", true);
561 ownValue = this._localName;
563 case Node.ATTRIBUTE_NODE:
564 ownValue = "@" + this._nodeName;
567 case Node.CDATA_SECTION_NODE:
570 case Node.PROCESSING_INSTRUCTION_NODE:
571 ownValue = "processing-instruction()";
573 case Node.COMMENT_NODE:
574 ownValue = "comment()";
576 case Node.DOCUMENT_NODE:
585 ownValue += "[" + ownIndex + "]";
587 return new WebInspector.DOMNode.XPathStep(ownValue, this._nodeType === Node.DOCUMENT_NODE);
593 _xPathIndex: function()
595 // Returns -1 in case of error, 0 if no siblings matching the same expression, <XPath index among the same expression-matching sibling nodes> otherwise.
596 function areNodesSimilar(left, right)
601 if (left._nodeType === Node.ELEMENT_NODE && right._nodeType === Node.ELEMENT_NODE)
602 return left._localName === right._localName;
604 if (left._nodeType === right._nodeType)
607 // XPath treats CDATA as text nodes.
608 var leftType = left._nodeType === Node.CDATA_SECTION_NODE ? Node.TEXT_NODE : left._nodeType;
609 var rightType = right._nodeType === Node.CDATA_SECTION_NODE ? Node.TEXT_NODE : right._nodeType;
610 return leftType === rightType;
613 var siblings = this.parentNode ? this.parentNode.children : null;
615 return 0; // Root node - no siblings.
616 var hasSameNamedElements;
617 for (var i = 0; i < siblings.length; ++i) {
618 if (areNodesSimilar(this, siblings[i]) && siblings[i] !== this) {
619 hasSameNamedElements = true;
623 if (!hasSameNamedElements)
625 var ownIndex = 1; // XPath indices start with 1.
626 for (var i = 0; i < siblings.length; ++i) {
627 if (areNodesSimilar(this, siblings[i])) {
628 if (siblings[i] === this)
633 return -1; // An error occurred: |this| not found in parent's children.
638 * @extends {WebInspector.DOMNode}
640 * @param {WebInspector.DOMAgent} domAgent
641 * @param {DOMAgent.Node} payload
643 WebInspector.DOMDocument = function(domAgent, payload)
645 WebInspector.DOMNode.call(this, domAgent, this, payload);
646 this.documentURL = payload.documentURL || "";
647 this.xmlVersion = payload.xmlVersion;
648 this._listeners = {};
651 WebInspector.DOMDocument.prototype.__proto__ = WebInspector.DOMNode.prototype;
654 * @extends {WebInspector.Object}
657 WebInspector.DOMAgent = function() {
658 /** @type {Object|undefined} */
659 this._idToDOMNode = {};
660 this._document = null;
661 this._attributeLoadNodeIds = {};
662 InspectorBackend.registerDOMDispatcher(new WebInspector.DOMDispatcher(this));
663 if (WebInspector.experimentsSettings.freeFlowDOMEditing.isEnabled())
664 new WebInspector.DOMModelResourceBinding(this);
666 if (WebInspector.settings.emulateTouchEvents.get())
667 this._emulateTouchEventsChanged();
668 WebInspector.settings.emulateTouchEvents.addChangeListener(this._emulateTouchEventsChanged, this);
671 WebInspector.DOMAgent.Events = {
672 AttrModified: "AttrModified",
673 AttrRemoved: "AttrRemoved",
674 CharacterDataModified: "CharacterDataModified",
675 NodeInserted: "NodeInserted",
676 NodeRemoved: "NodeRemoved",
677 DocumentUpdated: "DocumentUpdated",
678 ChildNodeCountUpdated: "ChildNodeCountUpdated",
679 InspectElementRequested: "InspectElementRequested",
680 StyleInvalidated: "StyleInvalidated",
681 UndoRedoRequested: "UndoRedoRequested",
682 UndoRedoCompleted: "UndoRedoCompleted"
685 WebInspector.DOMAgent.prototype = {
687 * @param {function(WebInspector.DOMDocument)=} callback
689 requestDocument: function(callback)
691 if (this._document) {
693 callback(this._document);
697 if (this._pendingDocumentRequestCallbacks) {
698 this._pendingDocumentRequestCallbacks.push(callback);
702 this._pendingDocumentRequestCallbacks = [callback];
705 * @this {WebInspector.DOMAgent}
706 * @param {?Protocol.Error} error
707 * @param {DOMAgent.Node} root
709 function onDocumentAvailable(error, root)
712 this._setDocument(root);
714 for (var i = 0; i < this._pendingDocumentRequestCallbacks.length; ++i) {
715 var callback = this._pendingDocumentRequestCallbacks[i];
717 callback(this._document);
719 delete this._pendingDocumentRequestCallbacks;
722 DOMAgent.getDocument(onDocumentAvailable.bind(this));
726 * @param {RuntimeAgent.RemoteObjectId} objectId
727 * @param {function()=} callback
729 pushNodeToFrontend: function(objectId, callback)
731 this._dispatchWhenDocumentAvailable(DOMAgent.requestNode.bind(DOMAgent, objectId), callback);
735 * @param {string} path
736 * @param {function(?WebInspector.DOMNode)=} callback
738 pushNodeByPathToFrontend: function(path, callback)
740 var callbackCast = /** @type {function(*)} */ callback;
741 this._dispatchWhenDocumentAvailable(DOMAgent.pushNodeByPathToFrontend.bind(DOMAgent, path), callbackCast);
745 * @param {function(*)=} callback
746 * @return {function(?Protocol.Error,*=)|undefined}
748 _wrapClientCallback: function(callback)
752 return function(error, result)
755 console.error("Error during DOMAgent operation: " + error);
756 callback(error ? null : result);
761 * @param {function(function()=)} func
762 * @param {function(*)=} callback
764 _dispatchWhenDocumentAvailable: function(func, callback)
766 var callbackWrapper = /** @type {function(?Protocol.Error, *=)} */ this._wrapClientCallback(callback);
768 function onDocumentAvailable()
771 func(callbackWrapper);
774 callbackWrapper("No document");
777 this.requestDocument(onDocumentAvailable.bind(this));
781 * @param {DOMAgent.NodeId} nodeId
782 * @param {string} name
783 * @param {string} value
785 _attributeModified: function(nodeId, name, value)
787 var node = this._idToDOMNode[nodeId];
790 node._setAttribute(name, value);
791 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.AttrModified, { node: node, name: name });
795 * @param {DOMAgent.NodeId} nodeId
796 * @param {string} name
798 _attributeRemoved: function(nodeId, name)
800 var node = this._idToDOMNode[nodeId];
803 node._removeAttribute(name);
804 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.AttrRemoved, { node: node, name: name });
808 * @param {Array.<DOMAgent.NodeId>} nodeIds
810 _inlineStyleInvalidated: function(nodeIds)
812 for (var i = 0; i < nodeIds.length; ++i)
813 this._attributeLoadNodeIds[nodeIds[i]] = true;
814 if ("_loadNodeAttributesTimeout" in this)
816 this._loadNodeAttributesTimeout = setTimeout(this._loadNodeAttributes.bind(this), 0);
819 _loadNodeAttributes: function()
822 * @this {WebInspector.DOMAgent}
823 * @param {DOMAgent.NodeId} nodeId
824 * @param {?Protocol.Error} error
825 * @param {Array.<string>} attributes
827 function callback(nodeId, error, attributes)
830 console.error("Error during DOMAgent operation: " + error);
833 var node = this._idToDOMNode[nodeId];
835 node._setAttributesPayload(attributes);
836 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.AttrModified, { node: node, name: "style" });
837 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.StyleInvalidated, node);
841 delete this._loadNodeAttributesTimeout;
843 for (var nodeId in this._attributeLoadNodeIds) {
844 var nodeIdAsNumber = parseInt(nodeId, 10);
845 DOMAgent.getAttributes(nodeIdAsNumber, callback.bind(this, nodeIdAsNumber));
847 this._attributeLoadNodeIds = {};
851 * @param {DOMAgent.NodeId} nodeId
852 * @param {string} newValue
854 _characterDataModified: function(nodeId, newValue)
856 var node = this._idToDOMNode[nodeId];
857 node._nodeValue = newValue;
858 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.CharacterDataModified, node);
862 * @param {DOMAgent.NodeId} nodeId
863 * @return {WebInspector.DOMNode|undefined}
865 nodeForId: function(nodeId)
867 return this._idToDOMNode[nodeId];
870 _documentUpdated: function()
872 this._setDocument(null);
876 * @param {DOMAgent.Node} payload
878 _setDocument: function(payload)
880 this._idToDOMNode = {};
881 if (payload && "nodeId" in payload)
882 this._document = new WebInspector.DOMDocument(this, payload);
884 this._document = null;
885 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.DocumentUpdated, this._document);
889 * @param {DOMAgent.Node} payload
891 _setDetachedRoot: function(payload)
893 new WebInspector.DOMNode(this, null, payload);
897 * @param {DOMAgent.NodeId} parentId
898 * @param {Array.<DOMAgent.Node>} payloads
900 _setChildNodes: function(parentId, payloads)
902 if (!parentId && payloads.length) {
903 this._setDetachedRoot(payloads[0]);
907 var parent = this._idToDOMNode[parentId];
908 parent._setChildrenPayload(payloads);
912 * @param {DOMAgent.NodeId} nodeId
913 * @param {number} newValue
915 _childNodeCountUpdated: function(nodeId, newValue)
917 var node = this._idToDOMNode[nodeId];
918 node._childNodeCount = newValue;
919 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.ChildNodeCountUpdated, node);
923 * @param {DOMAgent.NodeId} parentId
924 * @param {DOMAgent.NodeId} prevId
925 * @param {DOMAgent.Node} payload
927 _childNodeInserted: function(parentId, prevId, payload)
929 var parent = this._idToDOMNode[parentId];
930 var prev = this._idToDOMNode[prevId];
931 var node = parent._insertChild(prev, payload);
932 this._idToDOMNode[node.id] = node;
933 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.NodeInserted, node);
937 * @param {DOMAgent.NodeId} parentId
938 * @param {DOMAgent.NodeId} nodeId
940 _childNodeRemoved: function(parentId, nodeId)
942 var parent = this._idToDOMNode[parentId];
943 var node = this._idToDOMNode[nodeId];
944 parent._removeChild(node);
946 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.NodeRemoved, {node:node, parent:parent});
950 * @param {DOMAgent.Node} node
952 _unbind: function(node)
954 delete this._idToDOMNode[node.id];
955 for (var i = 0; node.children && i < node.children.length; ++i)
956 this._unbind(node.children[i]);
960 * @param {number} nodeId
962 inspectElement: function(nodeId)
964 var node = this._idToDOMNode[nodeId];
966 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.InspectElementRequested, node);
970 * @param {string} query
971 * @param {function(number)} searchCallback
973 performSearch: function(query, searchCallback)
978 * @param {?Protocol.Error} error
979 * @param {string} searchId
980 * @param {number} resultsCount
982 function callback(error, searchId, resultsCount)
984 this._searchId = searchId;
985 searchCallback(resultsCount);
987 DOMAgent.performSearch(query, callback.bind(this));
991 * @param {number} index
992 * @param {?function(DOMAgent.Node)} callback
994 searchResult: function(index, callback)
996 if (this._searchId) {
998 * @param {?Protocol.Error} error
999 * @param {Array.<number>} nodeIds
1001 function mycallback(error, nodeIds)
1004 console.error(error);
1008 if (nodeIds.length != 1)
1011 callback(this._idToDOMNode[nodeIds[0]]);
1013 DOMAgent.getSearchResults(this._searchId, index, index + 1, mycallback.bind(this));
1018 cancelSearch: function()
1020 if (this._searchId) {
1021 DOMAgent.discardSearchResults(this._searchId);
1022 delete this._searchId;
1027 * @param {DOMAgent.NodeId} nodeId
1028 * @param {string} selectors
1029 * @param {function(?DOMAgent.NodeId)=} callback
1031 querySelector: function(nodeId, selectors, callback)
1033 var callbackCast = /** @type {function(*)|undefined} */callback;
1034 DOMAgent.querySelector(nodeId, selectors, this._wrapClientCallback(callbackCast));
1038 * @param {DOMAgent.NodeId} nodeId
1039 * @param {string} selectors
1040 * @param {function(?Array.<DOMAgent.NodeId>)=} callback
1042 querySelectorAll: function(nodeId, selectors, callback)
1044 var callbackCast = /** @type {function(*)|undefined} */callback;
1045 DOMAgent.querySelectorAll(nodeId, selectors, this._wrapClientCallback(callbackCast));
1049 * @param {?number} nodeId
1050 * @param {string=} mode
1052 highlightDOMNode: function(nodeId, mode)
1054 if (this._hideDOMNodeHighlightTimeout) {
1055 clearTimeout(this._hideDOMNodeHighlightTimeout);
1056 delete this._hideDOMNodeHighlightTimeout;
1059 this._highlightedDOMNodeId = nodeId;
1061 DOMAgent.highlightNode(nodeId, this._buildHighlightConfig(mode));
1063 DOMAgent.hideHighlight();
1066 hideDOMNodeHighlight: function()
1068 this.highlightDOMNode(0);
1072 * @param {?DOMAgent.NodeId} nodeId
1074 highlightDOMNodeForTwoSeconds: function(nodeId)
1076 this.highlightDOMNode(nodeId);
1077 this._hideDOMNodeHighlightTimeout = setTimeout(this.hideDOMNodeHighlight.bind(this), 2000);
1081 * @param {boolean} enabled
1082 * @param {function()=} callback
1084 setInspectModeEnabled: function(enabled, callback)
1086 DOMAgent.setInspectModeEnabled(enabled, this._buildHighlightConfig(), callback);
1090 * @param {string=} mode
1092 _buildHighlightConfig: function(mode)
1094 mode = mode || "all";
1095 var highlightConfig = { showInfo: mode === "all" };
1096 if (mode === "all" || mode === "content")
1097 highlightConfig.contentColor = WebInspector.Color.PageHighlight.Content.toProtocolRGBA();
1099 if (mode === "all" || mode === "padding")
1100 highlightConfig.paddingColor = WebInspector.Color.PageHighlight.Padding.toProtocolRGBA();
1102 if (mode === "all" || mode === "border")
1103 highlightConfig.borderColor = WebInspector.Color.PageHighlight.Border.toProtocolRGBA();
1105 if (mode === "all" || mode === "margin")
1106 highlightConfig.marginColor = WebInspector.Color.PageHighlight.Margin.toProtocolRGBA();
1108 return highlightConfig;
1112 * @param {WebInspector.DOMNode} node
1113 * @param {function(?Protocol.Error)=} callback
1114 * @return {function(?Protocol.Error)}
1116 _markRevision: function(node, callback)
1118 function wrapperFunction(error)
1121 callback.apply(this, arguments);
1122 if (error || !WebInspector.experimentsSettings.freeFlowDOMEditing.isEnabled())
1124 if (this._captureDOMTimer)
1125 clearTimeout(this._captureDOMTimer);
1126 this._captureDOMTimer = setTimeout(this._captureDOM.bind(this, node), 500);
1128 return wrapperFunction.bind(this);
1132 * @param {WebInspector.DOMNode} node
1134 _captureDOM: function(node)
1136 delete this._captureDOMTimer;
1137 if (!node.ownerDocument)
1140 function callback(error, text)
1143 console.error(error);
1147 var url = node.ownerDocument.documentURL;
1151 var resource = WebInspector.resourceForURL(url);
1155 resource.addRevision(text);
1157 DOMAgent.getOuterHTML(node.ownerDocument.id, callback);
1161 _emulateTouchEventsChanged: function()
1163 DOMAgent.setTouchEmulationEnabled(WebInspector.settings.emulateTouchEvents.get());
1167 * @param {function(?Protocol.Error)=} callback
1169 undo: function(callback)
1171 function mycallback(error)
1173 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.UndoRedoCompleted);
1177 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.UndoRedoRequested);
1178 DOMAgent.undo(callback);
1182 * @param {function(?Protocol.Error)=} callback
1184 redo: function(callback)
1186 function mycallback(error)
1188 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.UndoRedoCompleted);
1192 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.UndoRedoRequested);
1193 DOMAgent.redo(callback);
1197 WebInspector.DOMAgent.prototype.__proto__ = WebInspector.Object.prototype;
1201 * @implements {DOMAgent.Dispatcher}
1202 * @param {WebInspector.DOMAgent} domAgent
1204 WebInspector.DOMDispatcher = function(domAgent)
1206 this._domAgent = domAgent;
1209 WebInspector.DOMDispatcher.prototype = {
1210 documentUpdated: function()
1212 this._domAgent._documentUpdated();
1216 * @param {DOMAgent.NodeId} nodeId
1217 * @param {string} name
1218 * @param {string} value
1220 attributeModified: function(nodeId, name, value)
1222 this._domAgent._attributeModified(nodeId, name, value);
1226 * @param {DOMAgent.NodeId} nodeId
1227 * @param {string} name
1229 attributeRemoved: function(nodeId, name)
1231 this._domAgent._attributeRemoved(nodeId, name);
1235 * @param {Array.<DOMAgent.NodeId>} nodeIds
1237 inlineStyleInvalidated: function(nodeIds)
1239 this._domAgent._inlineStyleInvalidated(nodeIds);
1243 * @param {DOMAgent.NodeId} nodeId
1244 * @param {string} characterData
1246 characterDataModified: function(nodeId, characterData)
1248 this._domAgent._characterDataModified(nodeId, characterData);
1252 * @param {DOMAgent.NodeId} parentId
1253 * @param {Array.<DOMAgent.Node>} payloads
1255 setChildNodes: function(parentId, payloads)
1257 this._domAgent._setChildNodes(parentId, payloads);
1261 * @param {DOMAgent.NodeId} nodeId
1262 * @param {number} childNodeCount
1264 childNodeCountUpdated: function(nodeId, childNodeCount)
1266 this._domAgent._childNodeCountUpdated(nodeId, childNodeCount);
1270 * @param {DOMAgent.NodeId} parentNodeId
1271 * @param {DOMAgent.NodeId} previousNodeId
1272 * @param {DOMAgent.Node} payload
1274 childNodeInserted: function(parentNodeId, previousNodeId, payload)
1276 this._domAgent._childNodeInserted(parentNodeId, previousNodeId, payload);
1280 * @param {DOMAgent.NodeId} parentNodeId
1281 * @param {DOMAgent.NodeId} nodeId
1283 childNodeRemoved: function(parentNodeId, nodeId)
1285 this._domAgent._childNodeRemoved(parentNodeId, nodeId);
1290 * @type {?WebInspector.DOMAgent}
1292 WebInspector.domAgent = null;
1296 * @implements {WebInspector.ResourceDomainModelBinding}
1298 WebInspector.DOMModelResourceBinding = function(domAgent)
1300 this._domAgent = domAgent;
1301 WebInspector.Resource.registerDomainModelBinding(WebInspector.Resource.Type.Document, this);
1304 WebInspector.DOMModelResourceBinding.prototype = {
1305 setContent: function(resource, content, majorChange, userCallback)
1307 var frameId = resource.frameId;
1311 PageAgent.setDocumentContent(frameId, content, callbackWrapper);
1313 function callbackWrapper(error)
1316 resource.addRevision(content);
1318 userCallback(error);
1322 canSetContent: function()
1328 WebInspector.DOMModelResourceBinding.prototype.__proto__ = WebInspector.ResourceDomainModelBinding.prototype;