Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / sdk / DOMModel.js
1 /*
2  * Copyright (C) 2009, 2010 Google Inc. All rights reserved.
3  * Copyright (C) 2009 Joseph Pecoraro
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
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
14  * distribution.
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.
18  *
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.
30  */
31
32 /**
33  * @constructor
34  * @extends {WebInspector.SDKObject}
35  * @param {!WebInspector.DOMModel} domModel
36  * @param {?WebInspector.DOMDocument} doc
37  * @param {boolean} isInShadowTree
38  * @param {!DOMAgent.Node} payload
39  */
40 WebInspector.DOMNode = function(domModel, doc, isInShadowTree, payload) {
41     WebInspector.SDKObject.call(this, domModel.target());
42     this._domModel = domModel;
43     this._agent = domModel._agent;
44     this.ownerDocument = doc;
45     this._isInShadowTree = isInShadowTree;
46
47     this.id = payload.nodeId;
48     domModel._idToDOMNode[this.id] = this;
49     this._nodeType = payload.nodeType;
50     this._nodeName = payload.nodeName;
51     this._localName = payload.localName;
52     this._nodeValue = payload.nodeValue;
53     this._pseudoType = payload.pseudoType;
54     this._shadowRootType = payload.shadowRootType;
55     this._frameId = payload.frameId || null;
56
57     this._shadowRoots = [];
58
59     this._attributes = [];
60     this._attributesMap = {};
61     if (payload.attributes)
62         this._setAttributesPayload(payload.attributes);
63
64     this._userProperties = {};
65     this._descendantUserPropertyCounters = {};
66
67     this._childNodeCount = payload.childNodeCount || 0;
68     this._children = null;
69
70     this.nextSibling = null;
71     this.previousSibling = null;
72     this.firstChild = null;
73     this.lastChild = null;
74     this.parentNode = null;
75
76     if (payload.shadowRoots) {
77         for (var i = 0; i < payload.shadowRoots.length; ++i) {
78             var root = payload.shadowRoots[i];
79             var node = new WebInspector.DOMNode(this._domModel, this.ownerDocument, true, root);
80             this._shadowRoots.push(node);
81             node.parentNode = this;
82         }
83     }
84
85     if (payload.templateContent) {
86         this._templateContent = new WebInspector.DOMNode(this._domModel, this.ownerDocument, true, payload.templateContent);
87         this._templateContent.parentNode = this;
88     }
89
90     if (payload.importedDocument) {
91         this._importedDocument = new WebInspector.DOMNode(this._domModel, this.ownerDocument, true, payload.importedDocument);
92         this._importedDocument.parentNode = this;
93     }
94
95     if (payload.children)
96         this._setChildrenPayload(payload.children);
97
98     this._setPseudoElements(payload.pseudoElements);
99
100     if (payload.contentDocument) {
101         this._contentDocument = new WebInspector.DOMDocument(domModel, payload.contentDocument);
102         this._children = [this._contentDocument];
103         this._renumber();
104     }
105
106     if (this._nodeType === Node.ELEMENT_NODE) {
107         // HTML and BODY from internal iframes should not overwrite top-level ones.
108         if (this.ownerDocument && !this.ownerDocument.documentElement && this._nodeName === "HTML")
109             this.ownerDocument.documentElement = this;
110         if (this.ownerDocument && !this.ownerDocument.body && this._nodeName === "BODY")
111             this.ownerDocument.body = this;
112     } else if (this._nodeType === Node.DOCUMENT_TYPE_NODE) {
113         this.publicId = payload.publicId;
114         this.systemId = payload.systemId;
115         this.internalSubset = payload.internalSubset;
116     } else if (this._nodeType === Node.ATTRIBUTE_NODE) {
117         this.name = payload.name;
118         this.value = payload.value;
119     }
120 }
121
122 /**
123  * @enum {string}
124  */
125 WebInspector.DOMNode.PseudoElementNames = {
126     Before: "before",
127     After: "after"
128 }
129
130 /**
131  * @enum {string}
132  */
133 WebInspector.DOMNode.ShadowRootTypes = {
134     UserAgent: "user-agent",
135     Author: "author"
136 }
137
138 WebInspector.DOMNode.prototype = {
139     /**
140      * @return {!WebInspector.DOMModel}
141      */
142     domModel: function()
143     {
144         return this._domModel;
145     },
146
147     /**
148      * @return {?Array.<!WebInspector.DOMNode>}
149      */
150     children: function()
151     {
152         return this._children ? this._children.slice() : null;
153     },
154
155     /**
156      * @return {boolean}
157      */
158     hasAttributes: function()
159     {
160         return this._attributes.length > 0;
161     },
162
163     /**
164      * @return {number}
165      */
166     childNodeCount: function()
167     {
168         return this._childNodeCount;
169     },
170
171     /**
172      * @return {boolean}
173      */
174     hasShadowRoots: function()
175     {
176         return !!this._shadowRoots.length;
177     },
178
179     /**
180      * @return {!Array.<!WebInspector.DOMNode>}
181      */
182     shadowRoots: function()
183     {
184         return this._shadowRoots.slice();
185     },
186
187     /**
188      * @return {?WebInspector.DOMNode}
189      */
190     templateContent: function()
191     {
192         return this._templateContent;
193     },
194
195     /**
196      * @return {?WebInspector.DOMNode}
197      */
198     importedDocument: function()
199     {
200         return this._importedDocument;
201     },
202
203     /**
204      * @return {number}
205      */
206     nodeType: function()
207     {
208         return this._nodeType;
209     },
210
211     /**
212      * @return {string}
213      */
214     nodeName: function()
215     {
216         return this._nodeName;
217     },
218
219     /**
220      * @return {string|undefined}
221      */
222     pseudoType: function()
223     {
224         return this._pseudoType;
225     },
226
227     /**
228      * @return {boolean}
229      */
230     hasPseudoElements: function()
231     {
232         return Object.keys(this._pseudoElements).length !== 0;
233     },
234
235     /**
236      * @return {!Object.<string, !WebInspector.DOMNode>}
237      */
238     pseudoElements: function()
239     {
240         return this._pseudoElements;
241     },
242
243     /**
244      * @return {boolean}
245      */
246     isInShadowTree: function()
247     {
248         return this._isInShadowTree;
249     },
250
251     /**
252      * @return {?WebInspector.DOMNode}
253      */
254     ancestorUserAgentShadowRoot: function()
255     {
256         if (!this._isInShadowTree)
257             return null;
258
259         var current = this;
260         while (!current.isShadowRoot())
261             current = current.parentNode;
262         return current.shadowRootType() === WebInspector.DOMNode.ShadowRootTypes.UserAgent ? current : null;
263     },
264
265     /**
266      * @return {boolean}
267      */
268     isShadowRoot: function()
269     {
270         return !!this._shadowRootType;
271     },
272
273     /**
274      * @return {?string}
275      */
276     shadowRootType: function()
277     {
278         return this._shadowRootType || null;
279     },
280
281     /**
282      * @return {string}
283      */
284     nodeNameInCorrectCase: function()
285     {
286         var shadowRootType = this.shadowRootType();
287         if (shadowRootType)
288             return "#shadow-root" + (shadowRootType === WebInspector.DOMNode.ShadowRootTypes.UserAgent ? " (user-agent)" : "");
289         return this.isXMLNode() ? this.nodeName() : this.nodeName().toLowerCase();
290     },
291
292     /**
293      * @param {string} name
294      * @param {function(?Protocol.Error, number)=} callback
295      */
296     setNodeName: function(name, callback)
297     {
298         this._agent.setNodeName(this.id, name, this._domModel._markRevision(this, callback));
299     },
300
301     /**
302      * @return {string}
303      */
304     localName: function()
305     {
306         return this._localName;
307     },
308
309     /**
310      * @return {string}
311      */
312     nodeValue: function()
313     {
314         return this._nodeValue;
315     },
316
317     /**
318      * @param {string} value
319      * @param {function(?Protocol.Error)=} callback
320      */
321     setNodeValue: function(value, callback)
322     {
323         this._agent.setNodeValue(this.id, value, this._domModel._markRevision(this, callback));
324     },
325
326     /**
327      * @param {string} name
328      * @return {string}
329      */
330     getAttribute: function(name)
331     {
332         var attr = this._attributesMap[name];
333         return attr ? attr.value : undefined;
334     },
335
336     /**
337      * @param {string} name
338      * @param {string} text
339      * @param {function(?Protocol.Error)=} callback
340      */
341     setAttribute: function(name, text, callback)
342     {
343         this._agent.setAttributesAsText(this.id, text, name, this._domModel._markRevision(this, callback));
344     },
345
346     /**
347      * @param {string} name
348      * @param {string} value
349      * @param {function(?Protocol.Error)=} callback
350      */
351     setAttributeValue: function(name, value, callback)
352     {
353         this._agent.setAttributeValue(this.id, name, value, this._domModel._markRevision(this, callback));
354     },
355
356     /**
357      * @return {!Object}
358      */
359     attributes: function()
360     {
361         return this._attributes;
362     },
363
364     /**
365      * @param {string} name
366      * @param {function(?Protocol.Error)=} callback
367      */
368     removeAttribute: function(name, callback)
369     {
370         /**
371          * @param {?Protocol.Error} error
372          * @this {WebInspector.DOMNode}
373          */
374         function mycallback(error)
375         {
376             if (!error) {
377                 delete this._attributesMap[name];
378                 for (var i = 0;  i < this._attributes.length; ++i) {
379                     if (this._attributes[i].name === name) {
380                         this._attributes.splice(i, 1);
381                         break;
382                     }
383                 }
384             }
385
386             this._domModel._markRevision(this, callback)(error);
387         }
388         this._agent.removeAttribute(this.id, name, mycallback.bind(this));
389     },
390
391     /**
392      * @param {function(?Array.<!WebInspector.DOMNode>)=} callback
393      */
394     getChildNodes: function(callback)
395     {
396         if (this._children) {
397             if (callback)
398                 callback(this.children());
399             return;
400         }
401
402         /**
403          * @this {WebInspector.DOMNode}
404          * @param {?Protocol.Error} error
405          */
406         function mycallback(error)
407         {
408             if (callback)
409                 callback(error ? null : this.children());
410         }
411
412         this._agent.requestChildNodes(this.id, undefined, mycallback.bind(this));
413     },
414
415     /**
416      * @param {number} depth
417      * @param {function(?Array.<!WebInspector.DOMNode>)=} callback
418      */
419     getSubtree: function(depth, callback)
420     {
421         /**
422          * @this {WebInspector.DOMNode}
423          * @param {?Protocol.Error} error
424          */
425         function mycallback(error)
426         {
427             if (callback)
428                 callback(error ? null : this._children);
429         }
430
431         this._agent.requestChildNodes(this.id, depth, mycallback.bind(this));
432     },
433
434     /**
435      * @param {function(?Protocol.Error, string)=} callback
436      */
437     getOuterHTML: function(callback)
438     {
439         this._agent.getOuterHTML(this.id, callback);
440     },
441
442     /**
443      * @param {string} html
444      * @param {function(?Protocol.Error)=} callback
445      */
446     setOuterHTML: function(html, callback)
447     {
448         this._agent.setOuterHTML(this.id, html, this._domModel._markRevision(this, callback));
449     },
450
451     /**
452      * @param {function(?Protocol.Error, !DOMAgent.NodeId=)=} callback
453      */
454     removeNode: function(callback)
455     {
456         this._agent.removeNode(this.id, this._domModel._markRevision(this, callback));
457     },
458
459     /**
460      * @param {function(?string)=} callback
461      */
462     copyNode: function(callback)
463     {
464         function copy(error, text)
465         {
466             if (!error)
467                 InspectorFrontendHost.copyText(text);
468             if (callback)
469                 callback(error ? null : text);
470         }
471         this._agent.getOuterHTML(this.id, copy);
472     },
473
474     /**
475      * @param {string} objectGroupId
476      * @param {function(?Array.<!WebInspector.DOMModel.EventListener>)} callback
477      */
478     eventListeners: function(objectGroupId, callback)
479     {
480         var target = this.target();
481
482         /**
483          * @param {?Protocol.Error} error
484          * @param {!Array.<!DOMAgent.EventListener>} payloads
485          */
486         function mycallback(error, payloads)
487         {
488             if (error) {
489                 callback(null);
490                 return;
491             }
492             callback(payloads.map(function(payload) {
493                 return new WebInspector.DOMModel.EventListener(target, payload);
494             }));
495         }
496         this._agent.getEventListenersForNode(this.id, objectGroupId, mycallback);
497     },
498
499     /**
500      * @return {string}
501      */
502     path: function()
503     {
504         /**
505          * @param {?WebInspector.DOMNode} node
506          */
507         function canPush(node)
508         {
509             return node && ("index" in node || (node.isShadowRoot() && node.parentNode)) && node._nodeName.length;
510         }
511
512         var path = [];
513         var node = this;
514         while (canPush(node)) {
515             var index = typeof node.index === "number" ? node.index : (node.shadowRootType() === WebInspector.DOMNode.ShadowRootTypes.UserAgent ? "u" : "a");
516             path.push([index, node._nodeName]);
517             node = node.parentNode;
518         }
519         path.reverse();
520         return path.join(",");
521     },
522
523     /**
524      * @param {!WebInspector.DOMNode} node
525      * @return {boolean}
526      */
527     isAncestor: function(node)
528     {
529         if (!node)
530             return false;
531
532         var currentNode = node.parentNode;
533         while (currentNode) {
534             if (this === currentNode)
535                 return true;
536             currentNode = currentNode.parentNode;
537         }
538         return false;
539     },
540
541     /**
542      * @param {!WebInspector.DOMNode} descendant
543      * @return {boolean}
544      */
545     isDescendant: function(descendant)
546     {
547         return descendant !== null && descendant.isAncestor(this);
548     },
549
550     /**
551      * @return {?PageAgent.FrameId}
552      */
553     frameId: function()
554     {
555         var node = this;
556         while (!node._frameId && node.parentNode)
557             node = node.parentNode;
558         return node._frameId;
559     },
560
561     /**
562      * @param {!Array.<string>} attrs
563      * @return {boolean}
564      */
565     _setAttributesPayload: function(attrs)
566     {
567         var attributesChanged = !this._attributes || attrs.length !== this._attributes.length * 2;
568         var oldAttributesMap = this._attributesMap || {};
569
570         this._attributes = [];
571         this._attributesMap = {};
572
573         for (var i = 0; i < attrs.length; i += 2) {
574             var name = attrs[i];
575             var value = attrs[i + 1];
576             this._addAttribute(name, value);
577
578             if (attributesChanged)
579                 continue;
580
581             if (!oldAttributesMap[name] || oldAttributesMap[name].value !== value)
582               attributesChanged = true;
583         }
584         return attributesChanged;
585     },
586
587     /**
588      * @param {!WebInspector.DOMNode} prev
589      * @param {!DOMAgent.Node} payload
590      * @return {!WebInspector.DOMNode}
591      */
592     _insertChild: function(prev, payload)
593     {
594         var node = new WebInspector.DOMNode(this._domModel, this.ownerDocument, this._isInShadowTree, payload);
595         this._children.splice(this._children.indexOf(prev) + 1, 0, node);
596         this._renumber();
597         return node;
598     },
599
600     /**
601      * @param {!WebInspector.DOMNode} node
602      */
603     _removeChild: function(node)
604     {
605         if (node.pseudoType()) {
606             delete this._pseudoElements[node.pseudoType()];
607         } else {
608             var shadowRootIndex = this._shadowRoots.indexOf(node);
609             if (shadowRootIndex !== -1)
610                 this._shadowRoots.splice(shadowRootIndex, 1);
611             else
612                 this._children.splice(this._children.indexOf(node), 1);
613         }
614         node.parentNode = null;
615         node._updateChildUserPropertyCountsOnRemoval(this);
616         this._renumber();
617     },
618
619     /**
620      * @param {!Array.<!DOMAgent.Node>} payloads
621      */
622     _setChildrenPayload: function(payloads)
623     {
624         // We set children in the constructor.
625         if (this._contentDocument)
626             return;
627
628         this._children = [];
629         for (var i = 0; i < payloads.length; ++i) {
630             var payload = payloads[i];
631             var node = new WebInspector.DOMNode(this._domModel, this.ownerDocument, this._isInShadowTree, payload);
632             this._children.push(node);
633         }
634         this._renumber();
635     },
636
637     /**
638      * @param {!Array.<!DOMAgent.Node>|undefined} payloads
639      */
640     _setPseudoElements: function(payloads)
641     {
642         this._pseudoElements = {};
643         if (!payloads)
644             return;
645
646         for (var i = 0; i < payloads.length; ++i) {
647             var node = new WebInspector.DOMNode(this._domModel, this.ownerDocument, this._isInShadowTree, payloads[i]);
648             node.parentNode = this;
649             this._pseudoElements[node.pseudoType()] = node;
650         }
651     },
652
653     _renumber: function()
654     {
655         this._childNodeCount = this._children.length;
656         if (this._childNodeCount == 0) {
657             this.firstChild = null;
658             this.lastChild = null;
659             return;
660         }
661         this.firstChild = this._children[0];
662         this.lastChild = this._children[this._childNodeCount - 1];
663         for (var i = 0; i < this._childNodeCount; ++i) {
664             var child = this._children[i];
665             child.index = i;
666             child.nextSibling = i + 1 < this._childNodeCount ? this._children[i + 1] : null;
667             child.previousSibling = i - 1 >= 0 ? this._children[i - 1] : null;
668             child.parentNode = this;
669         }
670     },
671
672     /**
673      * @param {string} name
674      * @param {string} value
675      */
676     _addAttribute: function(name, value)
677     {
678         var attr = {
679             name: name,
680             value: value,
681             _node: this
682         };
683         this._attributesMap[name] = attr;
684         this._attributes.push(attr);
685     },
686
687     /**
688      * @param {string} name
689      * @param {string} value
690      */
691     _setAttribute: function(name, value)
692     {
693         var attr = this._attributesMap[name];
694         if (attr)
695             attr.value = value;
696         else
697             this._addAttribute(name, value);
698     },
699
700     /**
701      * @param {string} name
702      */
703     _removeAttribute: function(name)
704     {
705         var attr = this._attributesMap[name];
706         if (attr) {
707             this._attributes.remove(attr);
708             delete this._attributesMap[name];
709         }
710     },
711
712     /**
713      * @param {!WebInspector.DOMNode} targetNode
714      * @param {?WebInspector.DOMNode} anchorNode
715      * @param {function(?Protocol.Error, !DOMAgent.NodeId=)=} callback
716      */
717     copyTo: function(targetNode, anchorNode, callback)
718     {
719         this._agent.copyTo(this.id, targetNode.id, anchorNode ? anchorNode.id : undefined, this._domModel._markRevision(this, callback));
720     },
721
722     /**
723      * @param {!WebInspector.DOMNode} targetNode
724      * @param {?WebInspector.DOMNode} anchorNode
725      * @param {function(?Protocol.Error, !DOMAgent.NodeId=)=} callback
726      */
727     moveTo: function(targetNode, anchorNode, callback)
728     {
729         this._agent.moveTo(this.id, targetNode.id, anchorNode ? anchorNode.id : undefined, this._domModel._markRevision(this, callback));
730     },
731
732     /**
733      * @return {boolean}
734      */
735     isXMLNode: function()
736     {
737         return !!this.ownerDocument && !!this.ownerDocument.xmlVersion;
738     },
739
740     _updateChildUserPropertyCountsOnRemoval: function(parentNode)
741     {
742         var result = {};
743         if (this._userProperties) {
744             for (var name in this._userProperties)
745                 result[name] = (result[name] || 0) + 1;
746         }
747
748         if (this._descendantUserPropertyCounters) {
749             for (var name in this._descendantUserPropertyCounters) {
750                 var counter = this._descendantUserPropertyCounters[name];
751                 result[name] = (result[name] || 0) + counter;
752             }
753         }
754
755         for (var name in result)
756             parentNode._updateDescendantUserPropertyCount(name, -result[name]);
757     },
758
759     _updateDescendantUserPropertyCount: function(name, delta)
760     {
761         if (!this._descendantUserPropertyCounters.hasOwnProperty(name))
762             this._descendantUserPropertyCounters[name] = 0;
763         this._descendantUserPropertyCounters[name] += delta;
764         if (!this._descendantUserPropertyCounters[name])
765             delete this._descendantUserPropertyCounters[name];
766         if (this.parentNode)
767             this.parentNode._updateDescendantUserPropertyCount(name, delta);
768     },
769
770     setUserProperty: function(name, value)
771     {
772         if (value === null) {
773             this.removeUserProperty(name);
774             return;
775         }
776
777         if (this.parentNode && !this._userProperties.hasOwnProperty(name))
778             this.parentNode._updateDescendantUserPropertyCount(name, 1);
779
780         this._userProperties[name] = value;
781     },
782
783     removeUserProperty: function(name)
784     {
785         if (!this._userProperties.hasOwnProperty(name))
786             return;
787
788         delete this._userProperties[name];
789         if (this.parentNode)
790             this.parentNode._updateDescendantUserPropertyCount(name, -1);
791     },
792
793     /**
794      * @param {string} name
795      * @return {?T}
796      * @template T
797      */
798     getUserProperty: function(name)
799     {
800         return (this._userProperties && this._userProperties[name]) || null;
801     },
802
803     /**
804      * @param {string} name
805      * @return {number}
806      */
807     descendantUserPropertyCount: function(name)
808     {
809         return this._descendantUserPropertyCounters && this._descendantUserPropertyCounters[name] ? this._descendantUserPropertyCounters[name] : 0;
810     },
811
812     /**
813      * @param {string} url
814      * @return {?string}
815      */
816     resolveURL: function(url)
817     {
818         if (!url)
819             return url;
820         for (var frameOwnerCandidate = this; frameOwnerCandidate; frameOwnerCandidate = frameOwnerCandidate.parentNode) {
821             if (frameOwnerCandidate.baseURL)
822                 return WebInspector.ParsedURL.completeURL(frameOwnerCandidate.baseURL, url);
823         }
824         return null;
825     },
826
827     /**
828      * @param {string=} mode
829      * @param {!RuntimeAgent.RemoteObjectId=} objectId
830      */
831     highlight: function(mode, objectId)
832     {
833         this._domModel.highlightDOMNode(this.id, mode, objectId);
834     },
835
836     highlightForTwoSeconds: function()
837     {
838         this._domModel.highlightDOMNodeForTwoSeconds(this.id);
839     },
840
841     /**
842      * @param {string=} objectGroup
843      * @param {function(?WebInspector.RemoteObject)=} callback
844      */
845     resolveToObject: function(objectGroup, callback)
846     {
847         this._agent.resolveNode(this.id, objectGroup, mycallback.bind(this));
848
849         /**
850          * @param {?Protocol.Error} error
851          * @param {!RuntimeAgent.RemoteObject} object
852          * @this {WebInspector.DOMNode}
853          */
854         function mycallback(error, object)
855         {
856             if (!callback)
857                 return;
858
859             if (error || !object)
860                 callback(null);
861             else
862                 callback(this.target().runtimeModel.createRemoteObject(object));
863         }
864     },
865
866     /**
867      * @param {function(?DOMAgent.BoxModel)} callback
868      */
869     boxModel: function(callback)
870     {
871         this._agent.getBoxModel(this.id, this._domModel._wrapClientCallback(callback));
872     },
873
874     __proto__: WebInspector.SDKObject.prototype
875 }
876
877 /**
878  * @param {!WebInspector.Target} target
879  * @param {number} backendNodeId
880  * @constructor
881  */
882 WebInspector.DeferredDOMNode = function(target, backendNodeId)
883 {
884     this._target = target;
885     this._backendNodeId = backendNodeId;
886 }
887
888 WebInspector.DeferredDOMNode.prototype = {
889     /**
890      * @param {function(?WebInspector.DOMNode)} callback
891      */
892     resolve: function(callback)
893     {
894         this._target.domModel.pushNodesByBackendIdsToFrontend([this._backendNodeId], onGotNode.bind(this));
895
896         /**
897          * @param {?Array.<number>} nodeIds
898          * @this {WebInspector.DeferredDOMNode}
899          */
900         function onGotNode(nodeIds)
901         {
902             if (!nodeIds || !nodeIds[0]) {
903                 callback(null);
904                 return;
905             }
906             callback(this._target.domModel.nodeForId(nodeIds[0]));
907         }
908     }
909 }
910
911 /**
912  * @extends {WebInspector.DOMNode}
913  * @constructor
914  * @param {!WebInspector.DOMModel} domModel
915  * @param {!DOMAgent.Node} payload
916  */
917 WebInspector.DOMDocument = function(domModel, payload)
918 {
919     WebInspector.DOMNode.call(this, domModel, this, false, payload);
920     this.documentURL = payload.documentURL || "";
921     this.baseURL = payload.baseURL || "";
922     this.xmlVersion = payload.xmlVersion;
923     this._listeners = {};
924 }
925
926 WebInspector.DOMDocument.prototype = {
927     __proto__: WebInspector.DOMNode.prototype
928 }
929
930 /**
931  * @constructor
932  * @extends {WebInspector.SDKModel}
933  * @param {!WebInspector.Target} target
934  */
935 WebInspector.DOMModel = function(target) {
936     WebInspector.SDKModel.call(this, WebInspector.DOMModel, target);
937
938     this._agent = target.domAgent();
939
940     /** @type {!Object.<number, !WebInspector.DOMNode>} */
941     this._idToDOMNode = {};
942     /** @type {?WebInspector.DOMDocument} */
943     this._document = null;
944     /** @type {!Object.<number, boolean>} */
945     this._attributeLoadNodeIds = {};
946     target.registerDOMDispatcher(new WebInspector.DOMDispatcher(this));
947
948     this._defaultHighlighter = new WebInspector.DefaultDOMNodeHighlighter(this._agent);
949     this._highlighter = this._defaultHighlighter;
950
951     if (Runtime.experiments.isEnabled("disableAgentsWhenProfile"))
952         WebInspector.profilingLock().addEventListener(WebInspector.Lock.Events.StateChanged, this._profilingStateChanged, this);
953
954     this._agent.enable();
955 }
956
957 WebInspector.DOMModel.Events = {
958     AttrModified: "AttrModified",
959     AttrRemoved: "AttrRemoved",
960     CharacterDataModified: "CharacterDataModified",
961     NodeInserted: "NodeInserted",
962     NodeInspected: "NodeInspected",
963     NodeRemoved: "NodeRemoved",
964     DocumentUpdated: "DocumentUpdated",
965     ChildNodeCountUpdated: "ChildNodeCountUpdated",
966     UndoRedoRequested: "UndoRedoRequested",
967     UndoRedoCompleted: "UndoRedoCompleted",
968 }
969
970 WebInspector.DOMModel.prototype = {
971     _profilingStateChanged: function()
972     {
973         if (WebInspector.profilingLock().isAcquired())
974             this._agent.disable();
975         else
976             this._agent.enable();
977     },
978
979     /**
980      * @param {function(!WebInspector.DOMDocument)=} callback
981      */
982     requestDocument: function(callback)
983     {
984         if (this._document) {
985             if (callback)
986                 callback(this._document);
987             return;
988         }
989
990         if (this._pendingDocumentRequestCallbacks) {
991             this._pendingDocumentRequestCallbacks.push(callback);
992             return;
993         }
994
995         this._pendingDocumentRequestCallbacks = [callback];
996
997         /**
998          * @this {WebInspector.DOMModel}
999          * @param {?Protocol.Error} error
1000          * @param {!DOMAgent.Node} root
1001          */
1002         function onDocumentAvailable(error, root)
1003         {
1004             if (!error)
1005                 this._setDocument(root);
1006
1007             for (var i = 0; i < this._pendingDocumentRequestCallbacks.length; ++i) {
1008                 var callback = this._pendingDocumentRequestCallbacks[i];
1009                 if (callback)
1010                     callback(this._document);
1011             }
1012             delete this._pendingDocumentRequestCallbacks;
1013         }
1014
1015         this._agent.getDocument(onDocumentAvailable.bind(this));
1016     },
1017
1018     /**
1019      * @return {?WebInspector.DOMDocument}
1020      */
1021     existingDocument: function()
1022     {
1023         return this._document;
1024     },
1025
1026     /**
1027      * @param {!RuntimeAgent.RemoteObjectId} objectId
1028      * @param {function(?WebInspector.DOMNode)=} callback
1029      */
1030     pushNodeToFrontend: function(objectId, callback)
1031     {
1032         /**
1033          * @param {?DOMAgent.NodeId} nodeId
1034          * @this {!WebInspector.DOMModel}
1035          */
1036         function mycallback(nodeId)
1037         {
1038             callback(nodeId ? this.nodeForId(nodeId) : null);
1039         }
1040         this._dispatchWhenDocumentAvailable(this._agent.requestNode.bind(this._agent, objectId), mycallback.bind(this));
1041     },
1042
1043     /**
1044      * @param {string} path
1045      * @param {function(?number)=} callback
1046      */
1047     pushNodeByPathToFrontend: function(path, callback)
1048     {
1049         this._dispatchWhenDocumentAvailable(this._agent.pushNodeByPathToFrontend.bind(this._agent, path), callback);
1050     },
1051
1052     /**
1053      * @param {!Array.<number>} backendNodeIds
1054      * @param {function(?Array.<number>)=} callback
1055      */
1056     pushNodesByBackendIdsToFrontend: function(backendNodeIds, callback)
1057     {
1058         this._dispatchWhenDocumentAvailable(this._agent.pushNodesByBackendIdsToFrontend.bind(this._agent, backendNodeIds), callback);
1059     },
1060
1061     /**
1062      * @param {function(!T)=} callback
1063      * @return {function(?Protocol.Error, !T=)|undefined}
1064      * @template T
1065      */
1066     _wrapClientCallback: function(callback)
1067     {
1068         if (!callback)
1069             return;
1070         /**
1071          * @param {?Protocol.Error} error
1072          * @param {!T=} result
1073          * @template T
1074          */
1075         var wrapper = function(error, result)
1076         {
1077             // Caller is responsible for handling the actual error.
1078             callback(error ? null : result);
1079         };
1080         return wrapper;
1081     },
1082
1083     /**
1084      * @param {function(function(?Protocol.Error, !T=)=)} func
1085      * @param {function(!T)=} callback
1086      * @template T
1087      */
1088     _dispatchWhenDocumentAvailable: function(func, callback)
1089     {
1090         var callbackWrapper = this._wrapClientCallback(callback);
1091
1092         /**
1093          * @this {WebInspector.DOMModel}
1094          */
1095         function onDocumentAvailable()
1096         {
1097             if (this._document)
1098                 func(callbackWrapper);
1099             else {
1100                 if (callbackWrapper)
1101                     callbackWrapper("No document");
1102             }
1103         }
1104         this.requestDocument(onDocumentAvailable.bind(this));
1105     },
1106
1107     /**
1108      * @param {!DOMAgent.NodeId} nodeId
1109      * @param {string} name
1110      * @param {string} value
1111      */
1112     _attributeModified: function(nodeId, name, value)
1113     {
1114         var node = this._idToDOMNode[nodeId];
1115         if (!node)
1116             return;
1117
1118         node._setAttribute(name, value);
1119         this.dispatchEventToListeners(WebInspector.DOMModel.Events.AttrModified, { node: node, name: name });
1120     },
1121
1122     /**
1123      * @param {!DOMAgent.NodeId} nodeId
1124      * @param {string} name
1125      */
1126     _attributeRemoved: function(nodeId, name)
1127     {
1128         var node = this._idToDOMNode[nodeId];
1129         if (!node)
1130             return;
1131         node._removeAttribute(name);
1132         this.dispatchEventToListeners(WebInspector.DOMModel.Events.AttrRemoved, { node: node, name: name });
1133     },
1134
1135     /**
1136      * @param {!Array.<!DOMAgent.NodeId>} nodeIds
1137      */
1138     _inlineStyleInvalidated: function(nodeIds)
1139     {
1140         for (var i = 0; i < nodeIds.length; ++i)
1141             this._attributeLoadNodeIds[nodeIds[i]] = true;
1142         if ("_loadNodeAttributesTimeout" in this)
1143             return;
1144         this._loadNodeAttributesTimeout = setTimeout(this._loadNodeAttributes.bind(this), 20);
1145     },
1146
1147     _loadNodeAttributes: function()
1148     {
1149         /**
1150          * @this {WebInspector.DOMModel}
1151          * @param {!DOMAgent.NodeId} nodeId
1152          * @param {?Protocol.Error} error
1153          * @param {!Array.<string>} attributes
1154          */
1155         function callback(nodeId, error, attributes)
1156         {
1157             if (error) {
1158                 // We are calling _loadNodeAttributes asynchronously, it is ok if node is not found.
1159                 return;
1160             }
1161             var node = this._idToDOMNode[nodeId];
1162             if (node) {
1163                 if (node._setAttributesPayload(attributes))
1164                     this.dispatchEventToListeners(WebInspector.DOMModel.Events.AttrModified, { node: node, name: "style" });
1165             }
1166         }
1167
1168         delete this._loadNodeAttributesTimeout;
1169
1170         for (var nodeId in this._attributeLoadNodeIds) {
1171             var nodeIdAsNumber = parseInt(nodeId, 10);
1172             this._agent.getAttributes(nodeIdAsNumber, callback.bind(this, nodeIdAsNumber));
1173         }
1174         this._attributeLoadNodeIds = {};
1175     },
1176
1177     /**
1178      * @param {!DOMAgent.NodeId} nodeId
1179      * @param {string} newValue
1180      */
1181     _characterDataModified: function(nodeId, newValue)
1182     {
1183         var node = this._idToDOMNode[nodeId];
1184         node._nodeValue = newValue;
1185         this.dispatchEventToListeners(WebInspector.DOMModel.Events.CharacterDataModified, node);
1186     },
1187
1188     /**
1189      * @param {!DOMAgent.NodeId} nodeId
1190      * @return {?WebInspector.DOMNode}
1191      */
1192     nodeForId: function(nodeId)
1193     {
1194         return this._idToDOMNode[nodeId] || null;
1195     },
1196
1197     _documentUpdated: function()
1198     {
1199         this._setDocument(null);
1200     },
1201
1202     /**
1203      * @param {?DOMAgent.Node} payload
1204      */
1205     _setDocument: function(payload)
1206     {
1207         this._idToDOMNode = {};
1208         if (payload && "nodeId" in payload)
1209             this._document = new WebInspector.DOMDocument(this, payload);
1210         else
1211             this._document = null;
1212         this.dispatchEventToListeners(WebInspector.DOMModel.Events.DocumentUpdated, this._document);
1213     },
1214
1215     /**
1216      * @param {!DOMAgent.Node} payload
1217      */
1218     _setDetachedRoot: function(payload)
1219     {
1220         if (payload.nodeName === "#document")
1221             new WebInspector.DOMDocument(this, payload);
1222         else
1223             new WebInspector.DOMNode(this, null, false, payload);
1224     },
1225
1226     /**
1227      * @param {!DOMAgent.NodeId} parentId
1228      * @param {!Array.<!DOMAgent.Node>} payloads
1229      */
1230     _setChildNodes: function(parentId, payloads)
1231     {
1232         if (!parentId && payloads.length) {
1233             this._setDetachedRoot(payloads[0]);
1234             return;
1235         }
1236
1237         var parent = this._idToDOMNode[parentId];
1238         parent._setChildrenPayload(payloads);
1239     },
1240
1241     /**
1242      * @param {!DOMAgent.NodeId} nodeId
1243      * @param {number} newValue
1244      */
1245     _childNodeCountUpdated: function(nodeId, newValue)
1246     {
1247         var node = this._idToDOMNode[nodeId];
1248         node._childNodeCount = newValue;
1249         this.dispatchEventToListeners(WebInspector.DOMModel.Events.ChildNodeCountUpdated, node);
1250     },
1251
1252     /**
1253      * @param {!DOMAgent.NodeId} parentId
1254      * @param {!DOMAgent.NodeId} prevId
1255      * @param {!DOMAgent.Node} payload
1256      */
1257     _childNodeInserted: function(parentId, prevId, payload)
1258     {
1259         var parent = this._idToDOMNode[parentId];
1260         var prev = this._idToDOMNode[prevId];
1261         var node = parent._insertChild(prev, payload);
1262         this._idToDOMNode[node.id] = node;
1263         this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeInserted, node);
1264     },
1265
1266     /**
1267      * @param {!DOMAgent.NodeId} parentId
1268      * @param {!DOMAgent.NodeId} nodeId
1269      */
1270     _childNodeRemoved: function(parentId, nodeId)
1271     {
1272         var parent = this._idToDOMNode[parentId];
1273         var node = this._idToDOMNode[nodeId];
1274         parent._removeChild(node);
1275         this._unbind(node);
1276         this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeRemoved, {node: node, parent: parent});
1277     },
1278
1279     /**
1280      * @param {!DOMAgent.NodeId} hostId
1281      * @param {!DOMAgent.Node} root
1282      */
1283     _shadowRootPushed: function(hostId, root)
1284     {
1285         var host = this._idToDOMNode[hostId];
1286         if (!host)
1287             return;
1288         var node = new WebInspector.DOMNode(this, host.ownerDocument, true, root);
1289         node.parentNode = host;
1290         this._idToDOMNode[node.id] = node;
1291         host._shadowRoots.push(node);
1292         this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeInserted, node);
1293     },
1294
1295     /**
1296      * @param {!DOMAgent.NodeId} hostId
1297      * @param {!DOMAgent.NodeId} rootId
1298      */
1299     _shadowRootPopped: function(hostId, rootId)
1300     {
1301         var host = this._idToDOMNode[hostId];
1302         if (!host)
1303             return;
1304         var root = this._idToDOMNode[rootId];
1305         if (!root)
1306             return;
1307         host._removeChild(root);
1308         this._unbind(root);
1309         this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeRemoved, {node: root, parent: host});
1310     },
1311
1312     /**
1313      * @param {!DOMAgent.NodeId} parentId
1314      * @param {!DOMAgent.Node} pseudoElement
1315      */
1316     _pseudoElementAdded: function(parentId, pseudoElement)
1317     {
1318         var parent = this._idToDOMNode[parentId];
1319         if (!parent)
1320             return;
1321         var node = new WebInspector.DOMNode(this, parent.ownerDocument, false, pseudoElement);
1322         node.parentNode = parent;
1323         this._idToDOMNode[node.id] = node;
1324         console.assert(!parent._pseudoElements[node.pseudoType()]);
1325         parent._pseudoElements[node.pseudoType()] = node;
1326         this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeInserted, node);
1327     },
1328
1329     /**
1330      * @param {!DOMAgent.NodeId} parentId
1331      * @param {!DOMAgent.NodeId} pseudoElementId
1332      */
1333     _pseudoElementRemoved: function(parentId, pseudoElementId)
1334     {
1335         var parent = this._idToDOMNode[parentId];
1336         if (!parent)
1337             return;
1338         var pseudoElement = this._idToDOMNode[pseudoElementId];
1339         if (!pseudoElement)
1340             return;
1341         parent._removeChild(pseudoElement);
1342         this._unbind(pseudoElement);
1343         this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeRemoved, {node: pseudoElement, parent: parent});
1344     },
1345
1346     /**
1347      * @param {!WebInspector.DOMNode} node
1348      */
1349     _unbind: function(node)
1350     {
1351         delete this._idToDOMNode[node.id];
1352         for (var i = 0; node._children && i < node._children.length; ++i)
1353             this._unbind(node._children[i]);
1354         for (var i = 0; i < node._shadowRoots.length; ++i)
1355             this._unbind(node._shadowRoots[i]);
1356         var pseudoElements = node.pseudoElements();
1357         for (var id in pseudoElements)
1358             this._unbind(pseudoElements[id]);
1359         if (node._templateContent)
1360             this._unbind(node._templateContent);
1361     },
1362
1363     /**
1364      * @param {!DOMAgent.NodeId} nodeId
1365      */
1366     _inspectNodeRequested: function(nodeId)
1367     {
1368         this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeInspected, this.nodeForId(nodeId));
1369     },
1370
1371     /**
1372      * @param {string} query
1373      * @param {boolean} includeUserAgentShadowDOM
1374      * @param {function(number)} searchCallback
1375      */
1376     performSearch: function(query, includeUserAgentShadowDOM, searchCallback)
1377     {
1378         this.cancelSearch();
1379
1380         /**
1381          * @param {?Protocol.Error} error
1382          * @param {string} searchId
1383          * @param {number} resultsCount
1384          * @this {WebInspector.DOMModel}
1385          */
1386         function callback(error, searchId, resultsCount)
1387         {
1388             this._searchId = searchId;
1389             searchCallback(resultsCount);
1390         }
1391         this._agent.performSearch(query, includeUserAgentShadowDOM, callback.bind(this));
1392     },
1393
1394     /**
1395      * @param {string} query
1396      * @param {boolean} includeUserAgentShadowDOM
1397      * @return {!Promise.<number>}
1398      */
1399     performSearchPromise: function(query, includeUserAgentShadowDOM)
1400     {
1401         return new Promise(performSearch.bind(this));
1402
1403         /**
1404          * @param {function(number)} resolve
1405          * @this {WebInspector.DOMModel}
1406          */
1407         function performSearch(resolve)
1408         {
1409             this._agent.performSearch(query, includeUserAgentShadowDOM, callback.bind(this));
1410
1411             /**
1412              * @param {?Protocol.Error} error
1413              * @param {string} searchId
1414              * @param {number} resultsCount
1415              * @this {WebInspector.DOMModel}
1416              */
1417             function callback(error, searchId, resultsCount)
1418             {
1419                 if (!error)
1420                     this._searchId = searchId;
1421                 resolve(error ? 0 : resultsCount);
1422             }
1423         }
1424     },
1425
1426     /**
1427      * @param {number} index
1428      * @param {?function(?WebInspector.DOMNode)} callback
1429      */
1430     searchResult: function(index, callback)
1431     {
1432         if (this._searchId)
1433             this._agent.getSearchResults(this._searchId, index, index + 1, searchResultsCallback.bind(this));
1434         else
1435             callback(null);
1436
1437         /**
1438          * @param {?Protocol.Error} error
1439          * @param {!Array.<number>} nodeIds
1440          * @this {WebInspector.DOMModel}
1441          */
1442         function searchResultsCallback(error, nodeIds)
1443         {
1444             if (error) {
1445                 console.error(error);
1446                 callback(null);
1447                 return;
1448             }
1449             if (nodeIds.length != 1)
1450                 return;
1451
1452             callback(this.nodeForId(nodeIds[0]));
1453         }
1454     },
1455
1456     cancelSearch: function()
1457     {
1458         if (this._searchId) {
1459             this._agent.discardSearchResults(this._searchId);
1460             delete this._searchId;
1461         }
1462     },
1463
1464     /**
1465      * @param {!DOMAgent.NodeId} nodeId
1466      * @param {string} selectors
1467      * @param {function(?DOMAgent.NodeId)=} callback
1468      */
1469     querySelector: function(nodeId, selectors, callback)
1470     {
1471         this._agent.querySelector(nodeId, selectors, this._wrapClientCallback(callback));
1472     },
1473
1474     /**
1475      * @param {!DOMAgent.NodeId} nodeId
1476      * @param {string} selectors
1477      * @param {function(!Array.<!DOMAgent.NodeId>=)=} callback
1478      */
1479     querySelectorAll: function(nodeId, selectors, callback)
1480     {
1481         this._agent.querySelectorAll(nodeId, selectors, this._wrapClientCallback(callback));
1482     },
1483
1484     /**
1485      * @param {!DOMAgent.NodeId=} nodeId
1486      * @param {string=} mode
1487      * @param {!RuntimeAgent.RemoteObjectId=} objectId
1488      */
1489     highlightDOMNode: function(nodeId, mode, objectId)
1490     {
1491         this.highlightDOMNodeWithConfig(nodeId, { mode: mode }, objectId);
1492     },
1493
1494     /**
1495      * @param {!DOMAgent.NodeId=} nodeId
1496      * @param {!{mode: (string|undefined), showInfo: (boolean|undefined)}=} config
1497      * @param {!RuntimeAgent.RemoteObjectId=} objectId
1498      */
1499     highlightDOMNodeWithConfig: function(nodeId, config, objectId)
1500     {
1501         config = config || { mode: "all", showInfo: undefined };
1502         if (this._hideDOMNodeHighlightTimeout) {
1503             clearTimeout(this._hideDOMNodeHighlightTimeout);
1504             delete this._hideDOMNodeHighlightTimeout;
1505         }
1506         var highlightConfig = this._buildHighlightConfig(config.mode);
1507         if (typeof config.showInfo !== "undefined")
1508             highlightConfig.showInfo = config.showInfo;
1509         this._highlighter.highlightDOMNode(this.nodeForId(nodeId || 0), highlightConfig, objectId);
1510     },
1511
1512     hideDOMNodeHighlight: function()
1513     {
1514         this.highlightDOMNode(0);
1515     },
1516
1517     /**
1518      * @param {!DOMAgent.NodeId} nodeId
1519      */
1520     highlightDOMNodeForTwoSeconds: function(nodeId)
1521     {
1522         this.highlightDOMNode(nodeId);
1523         this._hideDOMNodeHighlightTimeout = setTimeout(this.hideDOMNodeHighlight.bind(this), 2000);
1524     },
1525
1526     /**
1527      * @param {boolean} enabled
1528      * @param {boolean} inspectUAShadowDOM
1529      * @param {function(?Protocol.Error)=} callback
1530      */
1531     setInspectModeEnabled: function(enabled, inspectUAShadowDOM, callback)
1532     {
1533         /**
1534          * @this {WebInspector.DOMModel}
1535          */
1536         function onDocumentAvailable()
1537         {
1538             this._highlighter.setInspectModeEnabled(enabled, inspectUAShadowDOM, this._buildHighlightConfig(), callback);
1539         }
1540         this.requestDocument(onDocumentAvailable.bind(this));
1541     },
1542
1543     /**
1544      * @param {string=} mode
1545      * @return {!DOMAgent.HighlightConfig}
1546      */
1547     _buildHighlightConfig: function(mode)
1548     {
1549         mode = mode || "all";
1550         var highlightConfig = { showInfo: mode === "all", showRulers: WebInspector.overridesSupport.showMetricsRulers(), showExtensionLines: WebInspector.overridesSupport.showExtensionLines()};
1551         if (mode === "all" || mode === "content")
1552             highlightConfig.contentColor = WebInspector.Color.PageHighlight.Content.toProtocolRGBA();
1553
1554         if (mode === "all" || mode === "padding")
1555             highlightConfig.paddingColor = WebInspector.Color.PageHighlight.Padding.toProtocolRGBA();
1556
1557         if (mode === "all" || mode === "border")
1558             highlightConfig.borderColor = WebInspector.Color.PageHighlight.Border.toProtocolRGBA();
1559
1560         if (mode === "all" || mode === "margin")
1561             highlightConfig.marginColor = WebInspector.Color.PageHighlight.Margin.toProtocolRGBA();
1562
1563         if (mode === "all") {
1564             highlightConfig.eventTargetColor = WebInspector.Color.PageHighlight.EventTarget.toProtocolRGBA();
1565             highlightConfig.shapeColor = WebInspector.Color.PageHighlight.Shape.toProtocolRGBA();
1566             highlightConfig.shapeMarginColor = WebInspector.Color.PageHighlight.ShapeMargin.toProtocolRGBA();
1567         }
1568         return highlightConfig;
1569     },
1570
1571     /**
1572      * @param {!WebInspector.DOMNode} node
1573      * @param {function(?Protocol.Error, ...)=} callback
1574      * @return {function(...)}
1575      * @template T
1576      */
1577     _markRevision: function(node, callback)
1578     {
1579         /**
1580          * @param {?Protocol.Error} error
1581          * @this {WebInspector.DOMModel}
1582          */
1583         function wrapperFunction(error)
1584         {
1585             if (!error)
1586                 this.markUndoableState();
1587
1588             if (callback)
1589                 callback.apply(this, arguments);
1590         }
1591         return wrapperFunction.bind(this);
1592     },
1593
1594     /**
1595      * @param {boolean} emulationEnabled
1596      */
1597     emulateTouchEventObjects: function(emulationEnabled)
1598     {
1599         const injectedFunction = function() {
1600             const touchEvents = ["ontouchstart", "ontouchend", "ontouchmove", "ontouchcancel"];
1601             var recepients = [window.__proto__, document.__proto__];
1602             for (var i = 0; i < touchEvents.length; ++i) {
1603                 for (var j = 0; j < recepients.length; ++j) {
1604                     if (!(touchEvents[i] in recepients[j]))
1605                         Object.defineProperty(recepients[j], touchEvents[i], { value: null, writable: true, configurable: true, enumerable: true });
1606                 }
1607             }
1608         }
1609
1610         if (emulationEnabled && !this._addTouchEventsScriptInjecting) {
1611             this._addTouchEventsScriptInjecting = true;
1612             PageAgent.addScriptToEvaluateOnLoad("(" + injectedFunction.toString() + ")()", scriptAddedCallback.bind(this));
1613         } else {
1614             if (typeof this._addTouchEventsScriptId !== "undefined") {
1615                 PageAgent.removeScriptToEvaluateOnLoad(this._addTouchEventsScriptId);
1616                 delete this._addTouchEventsScriptId;
1617             }
1618         }
1619
1620         /**
1621          * @param {?Protocol.Error} error
1622          * @param {string} scriptId
1623          * @this {WebInspector.DOMModel}
1624          */
1625         function scriptAddedCallback(error, scriptId)
1626         {
1627             delete this._addTouchEventsScriptInjecting;
1628             if (error)
1629                 return;
1630             this._addTouchEventsScriptId = scriptId;
1631         }
1632
1633         PageAgent.setTouchEmulationEnabled(emulationEnabled);
1634     },
1635
1636     markUndoableState: function()
1637     {
1638         this._agent.markUndoableState();
1639     },
1640
1641     /**
1642      * @param {function(?Protocol.Error)=} callback
1643      */
1644     undo: function(callback)
1645     {
1646         /**
1647          * @param {?Protocol.Error} error
1648          * @this {WebInspector.DOMModel}
1649          */
1650         function mycallback(error)
1651         {
1652             this.dispatchEventToListeners(WebInspector.DOMModel.Events.UndoRedoCompleted);
1653             callback(error);
1654         }
1655
1656         this.dispatchEventToListeners(WebInspector.DOMModel.Events.UndoRedoRequested);
1657         this._agent.undo(callback);
1658     },
1659
1660     /**
1661      * @param {function(?Protocol.Error)=} callback
1662      */
1663     redo: function(callback)
1664     {
1665         /**
1666          * @param {?Protocol.Error} error
1667          * @this {WebInspector.DOMModel}
1668          */
1669         function mycallback(error)
1670         {
1671             this.dispatchEventToListeners(WebInspector.DOMModel.Events.UndoRedoCompleted);
1672             callback(error);
1673         }
1674
1675         this.dispatchEventToListeners(WebInspector.DOMModel.Events.UndoRedoRequested);
1676         this._agent.redo(callback);
1677     },
1678
1679     /**
1680      * @param {?WebInspector.DOMNodeHighlighter} highlighter
1681      */
1682     setHighlighter: function(highlighter)
1683     {
1684         this._highlighter = highlighter || this._defaultHighlighter;
1685     },
1686
1687     /**
1688      * @param {number} x
1689      * @param {number} y
1690      * @param {function(?WebInspector.DOMNode)} callback
1691      */
1692     nodeForLocation: function(x, y, callback)
1693     {
1694         this._agent.getNodeForLocation(x, y, mycallback.bind(this));
1695
1696         /**
1697          * @param {?Protocol.Error} error
1698          * @param {number} nodeId
1699          * @this {WebInspector.DOMModel}
1700          */
1701         function mycallback(error, nodeId)
1702         {
1703             if (error) {
1704                 callback(null);
1705                 return;
1706             }
1707             callback(this.nodeForId(nodeId));
1708         }
1709     },
1710
1711     __proto__: WebInspector.SDKModel.prototype
1712 }
1713
1714 /**
1715  * @constructor
1716  * @implements {DOMAgent.Dispatcher}
1717  * @param {!WebInspector.DOMModel} domModel
1718  */
1719 WebInspector.DOMDispatcher = function(domModel)
1720 {
1721     this._domModel = domModel;
1722 }
1723
1724 WebInspector.DOMDispatcher.prototype = {
1725     documentUpdated: function()
1726     {
1727         this._domModel._documentUpdated();
1728     },
1729
1730     /**
1731      * @param {!DOMAgent.NodeId} nodeId
1732      */
1733     inspectNodeRequested: function(nodeId)
1734     {
1735         this._domModel._inspectNodeRequested(nodeId);
1736     },
1737
1738     /**
1739      * @param {!DOMAgent.NodeId} nodeId
1740      * @param {string} name
1741      * @param {string} value
1742      */
1743     attributeModified: function(nodeId, name, value)
1744     {
1745         this._domModel._attributeModified(nodeId, name, value);
1746     },
1747
1748     /**
1749      * @param {!DOMAgent.NodeId} nodeId
1750      * @param {string} name
1751      */
1752     attributeRemoved: function(nodeId, name)
1753     {
1754         this._domModel._attributeRemoved(nodeId, name);
1755     },
1756
1757     /**
1758      * @param {!Array.<!DOMAgent.NodeId>} nodeIds
1759      */
1760     inlineStyleInvalidated: function(nodeIds)
1761     {
1762         this._domModel._inlineStyleInvalidated(nodeIds);
1763     },
1764
1765     /**
1766      * @param {!DOMAgent.NodeId} nodeId
1767      * @param {string} characterData
1768      */
1769     characterDataModified: function(nodeId, characterData)
1770     {
1771         this._domModel._characterDataModified(nodeId, characterData);
1772     },
1773
1774     /**
1775      * @param {!DOMAgent.NodeId} parentId
1776      * @param {!Array.<!DOMAgent.Node>} payloads
1777      */
1778     setChildNodes: function(parentId, payloads)
1779     {
1780         this._domModel._setChildNodes(parentId, payloads);
1781     },
1782
1783     /**
1784      * @param {!DOMAgent.NodeId} nodeId
1785      * @param {number} childNodeCount
1786      */
1787     childNodeCountUpdated: function(nodeId, childNodeCount)
1788     {
1789         this._domModel._childNodeCountUpdated(nodeId, childNodeCount);
1790     },
1791
1792     /**
1793      * @param {!DOMAgent.NodeId} parentNodeId
1794      * @param {!DOMAgent.NodeId} previousNodeId
1795      * @param {!DOMAgent.Node} payload
1796      */
1797     childNodeInserted: function(parentNodeId, previousNodeId, payload)
1798     {
1799         this._domModel._childNodeInserted(parentNodeId, previousNodeId, payload);
1800     },
1801
1802     /**
1803      * @param {!DOMAgent.NodeId} parentNodeId
1804      * @param {!DOMAgent.NodeId} nodeId
1805      */
1806     childNodeRemoved: function(parentNodeId, nodeId)
1807     {
1808         this._domModel._childNodeRemoved(parentNodeId, nodeId);
1809     },
1810
1811     /**
1812      * @param {!DOMAgent.NodeId} hostId
1813      * @param {!DOMAgent.Node} root
1814      */
1815     shadowRootPushed: function(hostId, root)
1816     {
1817         this._domModel._shadowRootPushed(hostId, root);
1818     },
1819
1820     /**
1821      * @param {!DOMAgent.NodeId} hostId
1822      * @param {!DOMAgent.NodeId} rootId
1823      */
1824     shadowRootPopped: function(hostId, rootId)
1825     {
1826         this._domModel._shadowRootPopped(hostId, rootId);
1827     },
1828
1829     /**
1830      * @param {!DOMAgent.NodeId} parentId
1831      * @param {!DOMAgent.Node} pseudoElement
1832      */
1833     pseudoElementAdded: function(parentId, pseudoElement)
1834     {
1835         this._domModel._pseudoElementAdded(parentId, pseudoElement);
1836     },
1837
1838     /**
1839      * @param {!DOMAgent.NodeId} parentId
1840      * @param {!DOMAgent.NodeId} pseudoElementId
1841      */
1842     pseudoElementRemoved: function(parentId, pseudoElementId)
1843     {
1844         this._domModel._pseudoElementRemoved(parentId, pseudoElementId);
1845     }
1846 }
1847
1848 /**
1849  * @constructor
1850  * @extends {WebInspector.SDKObject}
1851  * @param {!WebInspector.Target} target
1852  * @param {!DOMAgent.EventListener} payload
1853  */
1854 WebInspector.DOMModel.EventListener = function(target, payload)
1855 {
1856     WebInspector.SDKObject.call(this, target);
1857     this._payload = payload;
1858     var sourceName = this._payload.sourceName;
1859     if (!sourceName) {
1860         var script = target.debuggerModel.scriptForId(payload.location.scriptId);
1861         sourceName = script ? script.contentURL() : "";
1862     }
1863     this._sourceName = sourceName;
1864 }
1865
1866 WebInspector.DOMModel.EventListener.prototype = {
1867     /**
1868      * @return {!DOMAgent.EventListener}
1869      */
1870     payload: function()
1871     {
1872         return this._payload;
1873     },
1874
1875     /**
1876      * @return {?WebInspector.DOMNode}
1877      */
1878     node: function()
1879     {
1880         return this.target().domModel.nodeForId(this._payload.nodeId);
1881     },
1882
1883     /**
1884      * @return {!WebInspector.DebuggerModel.Location}
1885      */
1886     location: function()
1887     {
1888         return WebInspector.DebuggerModel.Location.fromPayload(this.target(), this._payload.location);
1889     },
1890
1891     /**
1892      * @return {?WebInspector.RemoteObject}
1893      */
1894     handler: function()
1895     {
1896         return this._payload.handler ? this.target().runtimeModel.createRemoteObject(this._payload.handler) : null;
1897     },
1898
1899     /**
1900      * @return {string}
1901      */
1902     sourceName: function()
1903     {
1904         return this._sourceName;
1905     },
1906
1907     __proto__: WebInspector.SDKObject.prototype
1908 }
1909
1910 /**
1911  * @interface
1912  */
1913 WebInspector.DOMNodeHighlighter = function() {
1914 }
1915
1916 WebInspector.DOMNodeHighlighter.prototype = {
1917     /**
1918      * @param {?WebInspector.DOMNode} node
1919      * @param {!DOMAgent.HighlightConfig} config
1920      * @param {!RuntimeAgent.RemoteObjectId=} objectId
1921      */
1922     highlightDOMNode: function(node, config, objectId) {},
1923
1924     /**
1925      * @param {boolean} enabled
1926      * @param {boolean} inspectUAShadowDOM
1927      * @param {!DOMAgent.HighlightConfig} config
1928      * @param {function(?Protocol.Error)=} callback
1929      */
1930     setInspectModeEnabled: function(enabled, inspectUAShadowDOM, config, callback) {}
1931 }
1932
1933 /**
1934  * @constructor
1935  * @implements {WebInspector.DOMNodeHighlighter}
1936  * @param {!Protocol.DOMAgent} agent
1937  */
1938 WebInspector.DefaultDOMNodeHighlighter = function(agent)
1939 {
1940     this._agent = agent;
1941 }
1942
1943 WebInspector.DefaultDOMNodeHighlighter.prototype = {
1944     /**
1945      * @param {?WebInspector.DOMNode} node
1946      * @param {!DOMAgent.HighlightConfig} config
1947      * @param {!RuntimeAgent.RemoteObjectId=} objectId
1948      */
1949     highlightDOMNode: function(node, config, objectId)
1950     {
1951         if (objectId || node)
1952             this._agent.highlightNode(config, objectId ? undefined : node.id, objectId);
1953         else
1954             this._agent.hideHighlight();
1955     },
1956
1957     /**
1958      * @param {boolean} enabled
1959      * @param {boolean} inspectUAShadowDOM
1960      * @param {!DOMAgent.HighlightConfig} config
1961      * @param {function(?Protocol.Error)=} callback
1962      */
1963     setInspectModeEnabled: function(enabled, inspectUAShadowDOM, config, callback)
1964     {
1965         WebInspector.overridesSupport.setTouchEmulationSuspended(enabled);
1966         this._agent.setInspectModeEnabled(enabled, inspectUAShadowDOM, config, callback);
1967     }
1968 }