Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(14)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/sdk/DOMModel.js

Issue 2466123002: DevTools: reformat front-end code to match chromium style. (Closed)
Patch Set: all done Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2009, 2010 Google Inc. All rights reserved. 2 * Copyright (C) 2009, 2010 Google Inc. All rights reserved.
3 * Copyright (C) 2009 Joseph Pecoraro 3 * Copyright (C) 2009 Joseph Pecoraro
4 * 4 *
5 * Redistribution and use in source and binary forms, with or without 5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are 6 * modification, are permitted provided that the following conditions are
7 * met: 7 * met:
8 * 8 *
9 * * Redistributions of source code must retain the above copyright 9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer. 10 * notice, this list of conditions and the following disclaimer.
(...skipping 10 matching lines...) Expand all
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 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. 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */ 30 */
31
32 /** 31 /**
33 * @constructor 32 * @unrestricted
34 * @extends {WebInspector.SDKObject}
35 * @param {!WebInspector.DOMModel} domModel
36 */ 33 */
37 WebInspector.DOMNode = function(domModel) 34 WebInspector.DOMNode = class extends WebInspector.SDKObject {
38 { 35 /**
39 WebInspector.SDKObject.call(this, domModel.target()); 36 * @param {!WebInspector.DOMModel} domModel
37 */
38 constructor(domModel) {
39 super(domModel.target());
40 this._domModel = domModel; 40 this._domModel = domModel;
41 }; 41 }
42 42
43 /** 43 /**
44 * @param {!WebInspector.DOMModel} domModel 44 * @param {!WebInspector.DOMModel} domModel
45 * @param {?WebInspector.DOMDocument} doc 45 * @param {?WebInspector.DOMDocument} doc
46 * @param {boolean} isInShadowTree 46 * @param {boolean} isInShadowTree
47 * @param {!DOMAgent.Node} payload 47 * @param {!DOMAgent.Node} payload
48 * @return {!WebInspector.DOMNode} 48 * @return {!WebInspector.DOMNode}
49 */ 49 */
50 WebInspector.DOMNode.create = function(domModel, doc, isInShadowTree, payload) 50 static create(domModel, doc, isInShadowTree, payload) {
51 {
52 var node = new WebInspector.DOMNode(domModel); 51 var node = new WebInspector.DOMNode(domModel);
53 node._init(doc, isInShadowTree, payload); 52 node._init(doc, isInShadowTree, payload);
54 return node; 53 return node;
55 } 54 }
55
56 /**
57 * @param {?WebInspector.DOMDocument} doc
58 * @param {boolean} isInShadowTree
59 * @param {!DOMAgent.Node} payload
60 */
61 _init(doc, isInShadowTree, payload) {
62 this._agent = this._domModel._agent;
63 this.ownerDocument = doc;
64 this._isInShadowTree = isInShadowTree;
65
66 this.id = payload.nodeId;
67 this._domModel._idToDOMNode[this.id] = this;
68 this._nodeType = payload.nodeType;
69 this._nodeName = payload.nodeName;
70 this._localName = payload.localName;
71 this._nodeValue = payload.nodeValue;
72 this._pseudoType = payload.pseudoType;
73 this._shadowRootType = payload.shadowRootType;
74 this._frameOwnerFrameId = payload.frameId || null;
75 this._xmlVersion = payload.xmlVersion;
76
77 this._shadowRoots = [];
78
79 this._attributes = [];
80 this._attributesMap = {};
81 if (payload.attributes)
82 this._setAttributesPayload(payload.attributes);
83
84 /** @type {!Map<string, ?>} */
85 this._markers = new Map();
86 this._subtreeMarkerCount = 0;
87
88 this._childNodeCount = payload.childNodeCount || 0;
89 this._children = null;
90
91 this.nextSibling = null;
92 this.previousSibling = null;
93 this.firstChild = null;
94 this.lastChild = null;
95 this.parentNode = null;
96
97 if (payload.shadowRoots) {
98 for (var i = 0; i < payload.shadowRoots.length; ++i) {
99 var root = payload.shadowRoots[i];
100 var node = WebInspector.DOMNode.create(this._domModel, this.ownerDocumen t, true, root);
101 this._shadowRoots.push(node);
102 node.parentNode = this;
103 }
104 }
105
106 if (payload.templateContent) {
107 this._templateContent =
108 WebInspector.DOMNode.create(this._domModel, this.ownerDocument, true, payload.templateContent);
109 this._templateContent.parentNode = this;
110 }
111
112 if (payload.importedDocument) {
113 this._importedDocument =
114 WebInspector.DOMNode.create(this._domModel, this.ownerDocument, true, payload.importedDocument);
115 this._importedDocument.parentNode = this;
116 }
117
118 if (payload.distributedNodes)
119 this._setDistributedNodePayloads(payload.distributedNodes);
120
121 if (payload.children)
122 this._setChildrenPayload(payload.children);
123
124 this._setPseudoElements(payload.pseudoElements);
125
126 if (payload.contentDocument) {
127 this._contentDocument = new WebInspector.DOMDocument(this._domModel, paylo ad.contentDocument);
128 this._children = [this._contentDocument];
129 this._renumber();
130 }
131
132 if (this._nodeType === Node.ELEMENT_NODE) {
133 // HTML and BODY from internal iframes should not overwrite top-level ones .
134 if (this.ownerDocument && !this.ownerDocument.documentElement && this._nod eName === 'HTML')
135 this.ownerDocument.documentElement = this;
136 if (this.ownerDocument && !this.ownerDocument.body && this._nodeName === ' BODY')
137 this.ownerDocument.body = this;
138 } else if (this._nodeType === Node.DOCUMENT_TYPE_NODE) {
139 this.publicId = payload.publicId;
140 this.systemId = payload.systemId;
141 this.internalSubset = payload.internalSubset;
142 } else if (this._nodeType === Node.ATTRIBUTE_NODE) {
143 this.name = payload.name;
144 this.value = payload.value;
145 }
146 }
147
148 /**
149 * @return {!WebInspector.DOMModel}
150 */
151 domModel() {
152 return this._domModel;
153 }
154
155 /**
156 * @return {?Array.<!WebInspector.DOMNode>}
157 */
158 children() {
159 return this._children ? this._children.slice() : null;
160 }
161
162 /**
163 * @return {boolean}
164 */
165 hasAttributes() {
166 return this._attributes.length > 0;
167 }
168
169 /**
170 * @return {number}
171 */
172 childNodeCount() {
173 return this._childNodeCount;
174 }
175
176 /**
177 * @return {boolean}
178 */
179 hasShadowRoots() {
180 return !!this._shadowRoots.length;
181 }
182
183 /**
184 * @return {!Array.<!WebInspector.DOMNode>}
185 */
186 shadowRoots() {
187 return this._shadowRoots.slice();
188 }
189
190 /**
191 * @return {?WebInspector.DOMNode}
192 */
193 templateContent() {
194 return this._templateContent || null;
195 }
196
197 /**
198 * @return {?WebInspector.DOMNode}
199 */
200 importedDocument() {
201 return this._importedDocument || null;
202 }
203
204 /**
205 * @return {number}
206 */
207 nodeType() {
208 return this._nodeType;
209 }
210
211 /**
212 * @return {string}
213 */
214 nodeName() {
215 return this._nodeName;
216 }
217
218 /**
219 * @return {string|undefined}
220 */
221 pseudoType() {
222 return this._pseudoType;
223 }
224
225 /**
226 * @return {boolean}
227 */
228 hasPseudoElements() {
229 return this._pseudoElements.size > 0;
230 }
231
232 /**
233 * @return {!Map<string, !WebInspector.DOMNode>}
234 */
235 pseudoElements() {
236 return this._pseudoElements;
237 }
238
239 /**
240 * @return {?WebInspector.DOMNode}
241 */
242 beforePseudoElement() {
243 if (!this._pseudoElements)
244 return null;
245 return this._pseudoElements.get(WebInspector.DOMNode.PseudoElementNames.Befo re);
246 }
247
248 /**
249 * @return {?WebInspector.DOMNode}
250 */
251 afterPseudoElement() {
252 if (!this._pseudoElements)
253 return null;
254 return this._pseudoElements.get(WebInspector.DOMNode.PseudoElementNames.Afte r);
255 }
256
257 /**
258 * @return {boolean}
259 */
260 isInsertionPoint() {
261 return !this.isXMLNode() &&
262 (this._nodeName === 'SHADOW' || this._nodeName === 'CONTENT' || this._no deName === 'SLOT');
263 }
264
265 /**
266 * @return {!Array.<!WebInspector.DOMNodeShortcut>}
267 */
268 distributedNodes() {
269 return this._distributedNodes || [];
270 }
271
272 /**
273 * @return {boolean}
274 */
275 isInShadowTree() {
276 return this._isInShadowTree;
277 }
278
279 /**
280 * @return {?WebInspector.DOMNode}
281 */
282 ancestorShadowHost() {
283 var ancestorShadowRoot = this.ancestorShadowRoot();
284 return ancestorShadowRoot ? ancestorShadowRoot.parentNode : null;
285 }
286
287 /**
288 * @return {?WebInspector.DOMNode}
289 */
290 ancestorShadowRoot() {
291 if (!this._isInShadowTree)
292 return null;
293
294 var current = this;
295 while (current && !current.isShadowRoot())
296 current = current.parentNode;
297 return current;
298 }
299
300 /**
301 * @return {?WebInspector.DOMNode}
302 */
303 ancestorUserAgentShadowRoot() {
304 var ancestorShadowRoot = this.ancestorShadowRoot();
305 if (!ancestorShadowRoot)
306 return null;
307 return ancestorShadowRoot.shadowRootType() === WebInspector.DOMNode.ShadowRo otTypes.UserAgent ? ancestorShadowRoot :
308 null;
309 }
310
311 /**
312 * @return {boolean}
313 */
314 isShadowRoot() {
315 return !!this._shadowRootType;
316 }
317
318 /**
319 * @return {?string}
320 */
321 shadowRootType() {
322 return this._shadowRootType || null;
323 }
324
325 /**
326 * @return {string}
327 */
328 nodeNameInCorrectCase() {
329 var shadowRootType = this.shadowRootType();
330 if (shadowRootType)
331 return '#shadow-root (' + shadowRootType + ')';
332
333 // If there is no local name, it's case sensitive
334 if (!this.localName())
335 return this.nodeName();
336
337 // If the names are different lengths, there is a prefix and it's case sensi tive
338 if (this.localName().length !== this.nodeName().length)
339 return this.nodeName();
340
341 // Return the localname, which will be case insensitive if its an html node
342 return this.localName();
343 }
344
345 /**
346 * @param {string} name
347 * @param {function(?Protocol.Error, number)=} callback
348 */
349 setNodeName(name, callback) {
350 this._agent.setNodeName(this.id, name, this._domModel._markRevision(this, ca llback));
351 }
352
353 /**
354 * @return {string}
355 */
356 localName() {
357 return this._localName;
358 }
359
360 /**
361 * @return {string}
362 */
363 nodeValue() {
364 return this._nodeValue;
365 }
366
367 /**
368 * @param {string} value
369 * @param {function(?Protocol.Error)=} callback
370 */
371 setNodeValue(value, callback) {
372 this._agent.setNodeValue(this.id, value, this._domModel._markRevision(this, callback));
373 }
374
375 /**
376 * @param {string} name
377 * @return {string}
378 */
379 getAttribute(name) {
380 var attr = this._attributesMap[name];
381 return attr ? attr.value : undefined;
382 }
383
384 /**
385 * @param {string} name
386 * @param {string} text
387 * @param {function(?Protocol.Error)=} callback
388 */
389 setAttribute(name, text, callback) {
390 this._agent.setAttributesAsText(this.id, text, name, this._domModel._markRev ision(this, callback));
391 }
392
393 /**
394 * @param {string} name
395 * @param {string} value
396 * @param {function(?Protocol.Error)=} callback
397 */
398 setAttributeValue(name, value, callback) {
399 this._agent.setAttributeValue(this.id, name, value, this._domModel._markRevi sion(this, callback));
400 }
401
402 /**
403 * @return {!Array<!WebInspector.DOMNode.Attribute>}
404 */
405 attributes() {
406 return this._attributes;
407 }
408
409 /**
410 * @param {string} name
411 * @param {function(?Protocol.Error)=} callback
412 */
413 removeAttribute(name, callback) {
414 /**
415 * @param {?Protocol.Error} error
416 * @this {WebInspector.DOMNode}
417 */
418 function mycallback(error) {
419 if (!error) {
420 delete this._attributesMap[name];
421 for (var i = 0; i < this._attributes.length; ++i) {
422 if (this._attributes[i].name === name) {
423 this._attributes.splice(i, 1);
424 break;
425 }
426 }
427 }
428
429 this._domModel._markRevision(this, callback)(error);
430 }
431 this._agent.removeAttribute(this.id, name, mycallback.bind(this));
432 }
433
434 /**
435 * @param {function(?Array.<!WebInspector.DOMNode>)=} callback
436 */
437 getChildNodes(callback) {
438 if (this._children) {
439 if (callback)
440 callback(this.children());
441 return;
442 }
443
444 /**
445 * @this {WebInspector.DOMNode}
446 * @param {?Protocol.Error} error
447 */
448 function mycallback(error) {
449 if (callback)
450 callback(error ? null : this.children());
451 }
452
453 this._agent.requestChildNodes(this.id, undefined, mycallback.bind(this));
454 }
455
456 /**
457 * @param {number} depth
458 * @param {function(?Array.<!WebInspector.DOMNode>)=} callback
459 */
460 getSubtree(depth, callback) {
461 /**
462 * @this {WebInspector.DOMNode}
463 * @param {?Protocol.Error} error
464 */
465 function mycallback(error) {
466 if (callback)
467 callback(error ? null : this._children);
468 }
469
470 this._agent.requestChildNodes(this.id, depth, mycallback.bind(this));
471 }
472
473 /**
474 * @param {function(?Protocol.Error, string)=} callback
475 */
476 getOuterHTML(callback) {
477 this._agent.getOuterHTML(this.id, callback);
478 }
479
480 /**
481 * @param {string} html
482 * @param {function(?Protocol.Error)=} callback
483 */
484 setOuterHTML(html, callback) {
485 this._agent.setOuterHTML(this.id, html, this._domModel._markRevision(this, c allback));
486 }
487
488 /**
489 * @param {function(?Protocol.Error, !DOMAgent.NodeId=)=} callback
490 */
491 removeNode(callback) {
492 this._agent.removeNode(this.id, this._domModel._markRevision(this, callback) );
493 }
494
495 /**
496 * @param {function(?string)=} callback
497 */
498 copyNode(callback) {
499 function copy(error, text) {
500 if (!error)
501 InspectorFrontendHost.copyText(text);
502 if (callback)
503 callback(error ? null : text);
504 }
505 this._agent.getOuterHTML(this.id, copy);
506 }
507
508 /**
509 * @return {string}
510 */
511 path() {
512 /**
513 * @param {?WebInspector.DOMNode} node
514 */
515 function canPush(node) {
516 return node && ('index' in node || (node.isShadowRoot() && node.parentNode )) && node._nodeName.length;
517 }
518
519 var path = [];
520 var node = this;
521 while (canPush(node)) {
522 var index = typeof node.index === 'number' ?
523 node.index :
524 (node.shadowRootType() === WebInspector.DOMNode.ShadowRootTypes.UserAg ent ? 'u' : 'a');
525 path.push([index, node._nodeName]);
526 node = node.parentNode;
527 }
528 path.reverse();
529 return path.join(',');
530 }
531
532 /**
533 * @param {!WebInspector.DOMNode} node
534 * @return {boolean}
535 */
536 isAncestor(node) {
537 if (!node)
538 return false;
539
540 var currentNode = node.parentNode;
541 while (currentNode) {
542 if (this === currentNode)
543 return true;
544 currentNode = currentNode.parentNode;
545 }
546 return false;
547 }
548
549 /**
550 * @param {!WebInspector.DOMNode} descendant
551 * @return {boolean}
552 */
553 isDescendant(descendant) {
554 return descendant !== null && descendant.isAncestor(this);
555 }
556
557 /**
558 * @return {?PageAgent.FrameId}
559 */
560 frameId() {
561 var node = this.parentNode || this;
562 while (!node._frameOwnerFrameId && node.parentNode)
563 node = node.parentNode;
564 return node._frameOwnerFrameId;
565 }
566
567 /**
568 * @param {!Array.<string>} attrs
569 * @return {boolean}
570 */
571 _setAttributesPayload(attrs) {
572 var attributesChanged = !this._attributes || attrs.length !== this._attribut es.length * 2;
573 var oldAttributesMap = this._attributesMap || {};
574
575 this._attributes = [];
576 this._attributesMap = {};
577
578 for (var i = 0; i < attrs.length; i += 2) {
579 var name = attrs[i];
580 var value = attrs[i + 1];
581 this._addAttribute(name, value);
582
583 if (attributesChanged)
584 continue;
585
586 if (!oldAttributesMap[name] || oldAttributesMap[name].value !== value)
587 attributesChanged = true;
588 }
589 return attributesChanged;
590 }
591
592 /**
593 * @param {!WebInspector.DOMNode} prev
594 * @param {!DOMAgent.Node} payload
595 * @return {!WebInspector.DOMNode}
596 */
597 _insertChild(prev, payload) {
598 var node = WebInspector.DOMNode.create(this._domModel, this.ownerDocument, t his._isInShadowTree, payload);
599 this._children.splice(this._children.indexOf(prev) + 1, 0, node);
600 this._renumber();
601 return node;
602 }
603
604 /**
605 * @param {!WebInspector.DOMNode} node
606 */
607 _removeChild(node) {
608 if (node.pseudoType()) {
609 this._pseudoElements.delete(node.pseudoType());
610 } else {
611 var shadowRootIndex = this._shadowRoots.indexOf(node);
612 if (shadowRootIndex !== -1) {
613 this._shadowRoots.splice(shadowRootIndex, 1);
614 } else {
615 console.assert(this._children.indexOf(node) !== -1);
616 this._children.splice(this._children.indexOf(node), 1);
617 }
618 }
619 node.parentNode = null;
620 this._subtreeMarkerCount -= node._subtreeMarkerCount;
621 if (node._subtreeMarkerCount)
622 this._domModel.dispatchEventToListeners(WebInspector.DOMModel.Events.Marke rsChanged, this);
623 this._renumber();
624 }
625
626 /**
627 * @param {!Array.<!DOMAgent.Node>} payloads
628 */
629 _setChildrenPayload(payloads) {
630 // We set children in the constructor.
631 if (this._contentDocument)
632 return;
633
634 this._children = [];
635 for (var i = 0; i < payloads.length; ++i) {
636 var payload = payloads[i];
637 var node = WebInspector.DOMNode.create(this._domModel, this.ownerDocument, this._isInShadowTree, payload);
638 this._children.push(node);
639 }
640 this._renumber();
641 }
642
643 /**
644 * @param {!Array.<!DOMAgent.Node>|undefined} payloads
645 */
646 _setPseudoElements(payloads) {
647 this._pseudoElements = new Map();
648 if (!payloads)
649 return;
650
651 for (var i = 0; i < payloads.length; ++i) {
652 var node = WebInspector.DOMNode.create(this._domModel, this.ownerDocument, this._isInShadowTree, payloads[i]);
653 node.parentNode = this;
654 this._pseudoElements.set(node.pseudoType(), node);
655 }
656 }
657
658 /**
659 * @param {!Array.<!DOMAgent.BackendNode>} payloads
660 */
661 _setDistributedNodePayloads(payloads) {
662 this._distributedNodes = [];
663 for (var payload of payloads)
664 this._distributedNodes.push(new WebInspector.DOMNodeShortcut(
665 this._domModel.target(), payload.backendNodeId, payload.nodeType, payl oad.nodeName));
666 }
667
668 _renumber() {
669 this._childNodeCount = this._children.length;
670 if (this._childNodeCount === 0) {
671 this.firstChild = null;
672 this.lastChild = null;
673 return;
674 }
675 this.firstChild = this._children[0];
676 this.lastChild = this._children[this._childNodeCount - 1];
677 for (var i = 0; i < this._childNodeCount; ++i) {
678 var child = this._children[i];
679 child.index = i;
680 child.nextSibling = i + 1 < this._childNodeCount ? this._children[i + 1] : null;
681 child.previousSibling = i - 1 >= 0 ? this._children[i - 1] : null;
682 child.parentNode = this;
683 }
684 }
685
686 /**
687 * @param {string} name
688 * @param {string} value
689 */
690 _addAttribute(name, value) {
691 var attr = {name: name, value: value, _node: this};
692 this._attributesMap[name] = attr;
693 this._attributes.push(attr);
694 }
695
696 /**
697 * @param {string} name
698 * @param {string} value
699 */
700 _setAttribute(name, value) {
701 var attr = this._attributesMap[name];
702 if (attr)
703 attr.value = value;
704 else
705 this._addAttribute(name, value);
706 }
707
708 /**
709 * @param {string} name
710 */
711 _removeAttribute(name) {
712 var attr = this._attributesMap[name];
713 if (attr) {
714 this._attributes.remove(attr);
715 delete this._attributesMap[name];
716 }
717 }
718
719 /**
720 * @param {!WebInspector.DOMNode} targetNode
721 * @param {?WebInspector.DOMNode} anchorNode
722 * @param {function(?Protocol.Error, !DOMAgent.NodeId=)=} callback
723 */
724 copyTo(targetNode, anchorNode, callback) {
725 this._agent.copyTo(
726 this.id, targetNode.id, anchorNode ? anchorNode.id : undefined, this._do mModel._markRevision(this, callback));
727 }
728
729 /**
730 * @param {!WebInspector.DOMNode} targetNode
731 * @param {?WebInspector.DOMNode} anchorNode
732 * @param {function(?Protocol.Error, !DOMAgent.NodeId=)=} callback
733 */
734 moveTo(targetNode, anchorNode, callback) {
735 this._agent.moveTo(
736 this.id, targetNode.id, anchorNode ? anchorNode.id : undefined, this._do mModel._markRevision(this, callback));
737 }
738
739 /**
740 * @return {boolean}
741 */
742 isXMLNode() {
743 return !!this._xmlVersion;
744 }
745
746 /**
747 * @param {string} name
748 * @param {?*} value
749 */
750 setMarker(name, value) {
751 if (value === null) {
752 if (!this._markers.has(name))
753 return;
754
755 this._markers.delete(name);
756 for (var node = this; node; node = node.parentNode)
757 --node._subtreeMarkerCount;
758 for (var node = this; node; node = node.parentNode)
759 this._domModel.dispatchEventToListeners(WebInspector.DOMModel.Events.Mar kersChanged, node);
760 return;
761 }
762
763 if (this.parentNode && !this._markers.has(name)) {
764 for (var node = this; node; node = node.parentNode)
765 ++node._subtreeMarkerCount;
766 }
767 this._markers.set(name, value);
768 for (var node = this; node; node = node.parentNode)
769 this._domModel.dispatchEventToListeners(WebInspector.DOMModel.Events.Marke rsChanged, node);
770 }
771
772 /**
773 * @param {string} name
774 * @return {?T}
775 * @template T
776 */
777 marker(name) {
778 return this._markers.get(name) || null;
779 }
780
781 /**
782 * @param {function(!WebInspector.DOMNode, string)} visitor
783 */
784 traverseMarkers(visitor) {
785 /**
786 * @param {!WebInspector.DOMNode} node
787 */
788 function traverse(node) {
789 if (!node._subtreeMarkerCount)
790 return;
791 for (var marker of node._markers.keys())
792 visitor(node, marker);
793 if (!node._children)
794 return;
795 for (var child of node._children)
796 traverse(child);
797 }
798 traverse(this);
799 }
800
801 /**
802 * @param {string} url
803 * @return {?string}
804 */
805 resolveURL(url) {
806 if (!url)
807 return url;
808 for (var frameOwnerCandidate = this; frameOwnerCandidate; frameOwnerCandidat e = frameOwnerCandidate.parentNode) {
809 if (frameOwnerCandidate.baseURL)
810 return WebInspector.ParsedURL.completeURL(frameOwnerCandidate.baseURL, u rl);
811 }
812 return null;
813 }
814
815 /**
816 * @param {string=} mode
817 * @param {!RuntimeAgent.RemoteObjectId=} objectId
818 */
819 highlight(mode, objectId) {
820 this._domModel.highlightDOMNode(this.id, mode, undefined, objectId);
821 }
822
823 highlightForTwoSeconds() {
824 this._domModel.highlightDOMNodeForTwoSeconds(this.id);
825 }
826
827 /**
828 * @param {string=} objectGroup
829 * @param {function(?WebInspector.RemoteObject)=} callback
830 */
831 resolveToObject(objectGroup, callback) {
832 this._agent.resolveNode(this.id, objectGroup, mycallback.bind(this));
833
834 /**
835 * @param {?Protocol.Error} error
836 * @param {!RuntimeAgent.RemoteObject} object
837 * @this {WebInspector.DOMNode}
838 */
839 function mycallback(error, object) {
840 if (!callback)
841 return;
842
843 if (error || !object)
844 callback(null);
845 else
846 callback(this.target().runtimeModel.createRemoteObject(object));
847 }
848 }
849
850 /**
851 * @param {string=} objectGroup
852 * @return {!Promise<!WebInspector.RemoteObject>}
853 */
854 resolveToObjectPromise(objectGroup) {
855 return new Promise(resolveToObject.bind(this));
856 /**
857 * @param {function(?)} fulfill
858 * @param {function(*)} reject
859 * @this {WebInspector.DOMNode}
860 */
861 function resolveToObject(fulfill, reject) {
862 this.resolveToObject(objectGroup, mycallback);
863 function mycallback(object) {
864 if (object)
865 fulfill(object);
866 else
867 reject(null);
868 }
869 }
870 }
871
872 /**
873 * @param {function(?DOMAgent.BoxModel)} callback
874 */
875 boxModel(callback) {
876 this._agent.getBoxModel(this.id, this._domModel._wrapClientCallback(callback ));
877 }
878
879 setAsInspectedNode() {
880 var node = this;
881 while (true) {
882 var ancestor = node.ancestorUserAgentShadowRoot();
883 if (!ancestor)
884 break;
885 ancestor = node.ancestorShadowHost();
886 if (!ancestor)
887 break;
888 // User agent shadow root, keep climbing up.
889 node = ancestor;
890 }
891 this._agent.setInspectedNode(node.id);
892 }
893
894 /**
895 * @return {?WebInspector.DOMNode}
896 */
897 enclosingElementOrSelf() {
898 var node = this;
899 if (node && node.nodeType() === Node.TEXT_NODE && node.parentNode)
900 node = node.parentNode;
901
902 if (node && node.nodeType() !== Node.ELEMENT_NODE)
903 node = null;
904 return node;
905 }
906 };
56 907
57 /** 908 /**
58 * @enum {string} 909 * @enum {string}
59 */ 910 */
60 WebInspector.DOMNode.PseudoElementNames = { 911 WebInspector.DOMNode.PseudoElementNames = {
61 Before: "before", 912 Before: 'before',
62 After: "after" 913 After: 'after'
63 }; 914 };
64 915
65 /** 916 /**
66 * @enum {string} 917 * @enum {string}
67 */ 918 */
68 WebInspector.DOMNode.ShadowRootTypes = { 919 WebInspector.DOMNode.ShadowRootTypes = {
69 UserAgent: "user-agent", 920 UserAgent: 'user-agent',
70 Open: "open", 921 Open: 'open',
71 Closed: "closed" 922 Closed: 'closed'
72 }; 923 };
73 924
74 /** @typedef {{name: string, value: string, _node: WebInspector.DOMNode}} */ 925 /** @typedef {{name: string, value: string, _node: WebInspector.DOMNode}} */
75 WebInspector.DOMNode.Attribute; 926 WebInspector.DOMNode.Attribute;
76 927
77 WebInspector.DOMNode.prototype = {
78 /**
79 * @param {?WebInspector.DOMDocument} doc
80 * @param {boolean} isInShadowTree
81 * @param {!DOMAgent.Node} payload
82 */
83 _init: function(doc, isInShadowTree, payload)
84 {
85 this._agent = this._domModel._agent;
86 this.ownerDocument = doc;
87 this._isInShadowTree = isInShadowTree;
88
89 this.id = payload.nodeId;
90 this._domModel._idToDOMNode[this.id] = this;
91 this._nodeType = payload.nodeType;
92 this._nodeName = payload.nodeName;
93 this._localName = payload.localName;
94 this._nodeValue = payload.nodeValue;
95 this._pseudoType = payload.pseudoType;
96 this._shadowRootType = payload.shadowRootType;
97 this._frameOwnerFrameId = payload.frameId || null;
98 this._xmlVersion = payload.xmlVersion;
99
100 this._shadowRoots = [];
101
102 this._attributes = [];
103 this._attributesMap = {};
104 if (payload.attributes)
105 this._setAttributesPayload(payload.attributes);
106
107 /** @type {!Map<string, ?>} */
108 this._markers = new Map();
109 this._subtreeMarkerCount = 0;
110
111 this._childNodeCount = payload.childNodeCount || 0;
112 this._children = null;
113
114 this.nextSibling = null;
115 this.previousSibling = null;
116 this.firstChild = null;
117 this.lastChild = null;
118 this.parentNode = null;
119
120 if (payload.shadowRoots) {
121 for (var i = 0; i < payload.shadowRoots.length; ++i) {
122 var root = payload.shadowRoots[i];
123 var node = WebInspector.DOMNode.create(this._domModel, this.owne rDocument, true, root);
124 this._shadowRoots.push(node);
125 node.parentNode = this;
126 }
127 }
128
129 if (payload.templateContent) {
130 this._templateContent = WebInspector.DOMNode.create(this._domModel, this.ownerDocument, true, payload.templateContent);
131 this._templateContent.parentNode = this;
132 }
133
134 if (payload.importedDocument) {
135 this._importedDocument = WebInspector.DOMNode.create(this._domModel, this.ownerDocument, true, payload.importedDocument);
136 this._importedDocument.parentNode = this;
137 }
138
139 if (payload.distributedNodes)
140 this._setDistributedNodePayloads(payload.distributedNodes);
141
142 if (payload.children)
143 this._setChildrenPayload(payload.children);
144
145 this._setPseudoElements(payload.pseudoElements);
146
147 if (payload.contentDocument) {
148 this._contentDocument = new WebInspector.DOMDocument(this._domModel, payload.contentDocument);
149 this._children = [this._contentDocument];
150 this._renumber();
151 }
152
153 if (this._nodeType === Node.ELEMENT_NODE) {
154 // HTML and BODY from internal iframes should not overwrite top-leve l ones.
155 if (this.ownerDocument && !this.ownerDocument.documentElement && thi s._nodeName === "HTML")
156 this.ownerDocument.documentElement = this;
157 if (this.ownerDocument && !this.ownerDocument.body && this._nodeName === "BODY")
158 this.ownerDocument.body = this;
159 } else if (this._nodeType === Node.DOCUMENT_TYPE_NODE) {
160 this.publicId = payload.publicId;
161 this.systemId = payload.systemId;
162 this.internalSubset = payload.internalSubset;
163 } else if (this._nodeType === Node.ATTRIBUTE_NODE) {
164 this.name = payload.name;
165 this.value = payload.value;
166 }
167 },
168
169 /**
170 * @return {!WebInspector.DOMModel}
171 */
172 domModel: function()
173 {
174 return this._domModel;
175 },
176
177 /**
178 * @return {?Array.<!WebInspector.DOMNode>}
179 */
180 children: function()
181 {
182 return this._children ? this._children.slice() : null;
183 },
184
185 /**
186 * @return {boolean}
187 */
188 hasAttributes: function()
189 {
190 return this._attributes.length > 0;
191 },
192
193 /**
194 * @return {number}
195 */
196 childNodeCount: function()
197 {
198 return this._childNodeCount;
199 },
200
201 /**
202 * @return {boolean}
203 */
204 hasShadowRoots: function()
205 {
206 return !!this._shadowRoots.length;
207 },
208
209 /**
210 * @return {!Array.<!WebInspector.DOMNode>}
211 */
212 shadowRoots: function()
213 {
214 return this._shadowRoots.slice();
215 },
216
217 /**
218 * @return {?WebInspector.DOMNode}
219 */
220 templateContent: function()
221 {
222 return this._templateContent || null;
223 },
224
225 /**
226 * @return {?WebInspector.DOMNode}
227 */
228 importedDocument: function()
229 {
230 return this._importedDocument || null;
231 },
232
233 /**
234 * @return {number}
235 */
236 nodeType: function()
237 {
238 return this._nodeType;
239 },
240
241 /**
242 * @return {string}
243 */
244 nodeName: function()
245 {
246 return this._nodeName;
247 },
248
249 /**
250 * @return {string|undefined}
251 */
252 pseudoType: function()
253 {
254 return this._pseudoType;
255 },
256
257 /**
258 * @return {boolean}
259 */
260 hasPseudoElements: function()
261 {
262 return this._pseudoElements.size > 0;
263 },
264
265 /**
266 * @return {!Map<string, !WebInspector.DOMNode>}
267 */
268 pseudoElements: function()
269 {
270 return this._pseudoElements;
271 },
272
273 /**
274 * @return {?WebInspector.DOMNode}
275 */
276 beforePseudoElement: function()
277 {
278 if (!this._pseudoElements)
279 return null;
280 return this._pseudoElements.get(WebInspector.DOMNode.PseudoElementNames. Before);
281 },
282
283 /**
284 * @return {?WebInspector.DOMNode}
285 */
286 afterPseudoElement: function()
287 {
288 if (!this._pseudoElements)
289 return null;
290 return this._pseudoElements.get(WebInspector.DOMNode.PseudoElementNames. After);
291 },
292
293 /**
294 * @return {boolean}
295 */
296 isInsertionPoint: function()
297 {
298 return !this.isXMLNode() && (this._nodeName === "SHADOW" || this._nodeNa me === "CONTENT" || this._nodeName === "SLOT");
299 },
300
301 /**
302 * @return {!Array.<!WebInspector.DOMNodeShortcut>}
303 */
304 distributedNodes: function()
305 {
306 return this._distributedNodes || [];
307 },
308
309 /**
310 * @return {boolean}
311 */
312 isInShadowTree: function()
313 {
314 return this._isInShadowTree;
315 },
316
317 /**
318 * @return {?WebInspector.DOMNode}
319 */
320 ancestorShadowHost: function()
321 {
322 var ancestorShadowRoot = this.ancestorShadowRoot();
323 return ancestorShadowRoot ? ancestorShadowRoot.parentNode : null;
324 },
325
326 /**
327 * @return {?WebInspector.DOMNode}
328 */
329 ancestorShadowRoot: function()
330 {
331 if (!this._isInShadowTree)
332 return null;
333
334 var current = this;
335 while (current && !current.isShadowRoot())
336 current = current.parentNode;
337 return current;
338 },
339
340 /**
341 * @return {?WebInspector.DOMNode}
342 */
343 ancestorUserAgentShadowRoot: function()
344 {
345 var ancestorShadowRoot = this.ancestorShadowRoot();
346 if (!ancestorShadowRoot)
347 return null;
348 return ancestorShadowRoot.shadowRootType() === WebInspector.DOMNode.Shad owRootTypes.UserAgent ? ancestorShadowRoot : null;
349 },
350
351 /**
352 * @return {boolean}
353 */
354 isShadowRoot: function()
355 {
356 return !!this._shadowRootType;
357 },
358
359 /**
360 * @return {?string}
361 */
362 shadowRootType: function()
363 {
364 return this._shadowRootType || null;
365 },
366
367 /**
368 * @return {string}
369 */
370 nodeNameInCorrectCase: function()
371 {
372 var shadowRootType = this.shadowRootType();
373 if (shadowRootType)
374 return "#shadow-root (" + shadowRootType + ")";
375
376 // If there is no local name, it's case sensitive
377 if (!this.localName())
378 return this.nodeName();
379
380 // If the names are different lengths, there is a prefix and it's case s ensitive
381 if (this.localName().length !== this.nodeName().length)
382 return this.nodeName();
383
384 // Return the localname, which will be case insensitive if its an html n ode
385 return this.localName();
386 },
387
388 /**
389 * @param {string} name
390 * @param {function(?Protocol.Error, number)=} callback
391 */
392 setNodeName: function(name, callback)
393 {
394 this._agent.setNodeName(this.id, name, this._domModel._markRevision(this , callback));
395 },
396
397 /**
398 * @return {string}
399 */
400 localName: function()
401 {
402 return this._localName;
403 },
404
405 /**
406 * @return {string}
407 */
408 nodeValue: function()
409 {
410 return this._nodeValue;
411 },
412
413 /**
414 * @param {string} value
415 * @param {function(?Protocol.Error)=} callback
416 */
417 setNodeValue: function(value, callback)
418 {
419 this._agent.setNodeValue(this.id, value, this._domModel._markRevision(th is, callback));
420 },
421
422 /**
423 * @param {string} name
424 * @return {string}
425 */
426 getAttribute: function(name)
427 {
428 var attr = this._attributesMap[name];
429 return attr ? attr.value : undefined;
430 },
431
432 /**
433 * @param {string} name
434 * @param {string} text
435 * @param {function(?Protocol.Error)=} callback
436 */
437 setAttribute: function(name, text, callback)
438 {
439 this._agent.setAttributesAsText(this.id, text, name, this._domModel._mar kRevision(this, callback));
440 },
441
442 /**
443 * @param {string} name
444 * @param {string} value
445 * @param {function(?Protocol.Error)=} callback
446 */
447 setAttributeValue: function(name, value, callback)
448 {
449 this._agent.setAttributeValue(this.id, name, value, this._domModel._mark Revision(this, callback));
450 },
451
452 /**
453 * @return {!Array<!WebInspector.DOMNode.Attribute>}
454 */
455 attributes: function()
456 {
457 return this._attributes;
458 },
459
460 /**
461 * @param {string} name
462 * @param {function(?Protocol.Error)=} callback
463 */
464 removeAttribute: function(name, callback)
465 {
466 /**
467 * @param {?Protocol.Error} error
468 * @this {WebInspector.DOMNode}
469 */
470 function mycallback(error)
471 {
472 if (!error) {
473 delete this._attributesMap[name];
474 for (var i = 0; i < this._attributes.length; ++i) {
475 if (this._attributes[i].name === name) {
476 this._attributes.splice(i, 1);
477 break;
478 }
479 }
480 }
481
482 this._domModel._markRevision(this, callback)(error);
483 }
484 this._agent.removeAttribute(this.id, name, mycallback.bind(this));
485 },
486
487 /**
488 * @param {function(?Array.<!WebInspector.DOMNode>)=} callback
489 */
490 getChildNodes: function(callback)
491 {
492 if (this._children) {
493 if (callback)
494 callback(this.children());
495 return;
496 }
497
498 /**
499 * @this {WebInspector.DOMNode}
500 * @param {?Protocol.Error} error
501 */
502 function mycallback(error)
503 {
504 if (callback)
505 callback(error ? null : this.children());
506 }
507
508 this._agent.requestChildNodes(this.id, undefined, mycallback.bind(this)) ;
509 },
510
511 /**
512 * @param {number} depth
513 * @param {function(?Array.<!WebInspector.DOMNode>)=} callback
514 */
515 getSubtree: function(depth, callback)
516 {
517 /**
518 * @this {WebInspector.DOMNode}
519 * @param {?Protocol.Error} error
520 */
521 function mycallback(error)
522 {
523 if (callback)
524 callback(error ? null : this._children);
525 }
526
527 this._agent.requestChildNodes(this.id, depth, mycallback.bind(this));
528 },
529
530 /**
531 * @param {function(?Protocol.Error, string)=} callback
532 */
533 getOuterHTML: function(callback)
534 {
535 this._agent.getOuterHTML(this.id, callback);
536 },
537
538 /**
539 * @param {string} html
540 * @param {function(?Protocol.Error)=} callback
541 */
542 setOuterHTML: function(html, callback)
543 {
544 this._agent.setOuterHTML(this.id, html, this._domModel._markRevision(thi s, callback));
545 },
546
547 /**
548 * @param {function(?Protocol.Error, !DOMAgent.NodeId=)=} callback
549 */
550 removeNode: function(callback)
551 {
552 this._agent.removeNode(this.id, this._domModel._markRevision(this, callb ack));
553 },
554
555 /**
556 * @param {function(?string)=} callback
557 */
558 copyNode: function(callback)
559 {
560 function copy(error, text)
561 {
562 if (!error)
563 InspectorFrontendHost.copyText(text);
564 if (callback)
565 callback(error ? null : text);
566 }
567 this._agent.getOuterHTML(this.id, copy);
568 },
569
570 /**
571 * @return {string}
572 */
573 path: function()
574 {
575 /**
576 * @param {?WebInspector.DOMNode} node
577 */
578 function canPush(node)
579 {
580 return node && ("index" in node || (node.isShadowRoot() && node.pare ntNode)) && node._nodeName.length;
581 }
582
583 var path = [];
584 var node = this;
585 while (canPush(node)) {
586 var index = typeof node.index === "number" ? node.index : (node.shad owRootType() === WebInspector.DOMNode.ShadowRootTypes.UserAgent ? "u" : "a");
587 path.push([index, node._nodeName]);
588 node = node.parentNode;
589 }
590 path.reverse();
591 return path.join(",");
592 },
593
594 /**
595 * @param {!WebInspector.DOMNode} node
596 * @return {boolean}
597 */
598 isAncestor: function(node)
599 {
600 if (!node)
601 return false;
602
603 var currentNode = node.parentNode;
604 while (currentNode) {
605 if (this === currentNode)
606 return true;
607 currentNode = currentNode.parentNode;
608 }
609 return false;
610 },
611
612 /**
613 * @param {!WebInspector.DOMNode} descendant
614 * @return {boolean}
615 */
616 isDescendant: function(descendant)
617 {
618 return descendant !== null && descendant.isAncestor(this);
619 },
620
621 /**
622 * @return {?PageAgent.FrameId}
623 */
624 frameId: function()
625 {
626 var node = this.parentNode || this;
627 while (!node._frameOwnerFrameId && node.parentNode)
628 node = node.parentNode;
629 return node._frameOwnerFrameId;
630 },
631
632 /**
633 * @param {!Array.<string>} attrs
634 * @return {boolean}
635 */
636 _setAttributesPayload: function(attrs)
637 {
638 var attributesChanged = !this._attributes || attrs.length !== this._attr ibutes.length * 2;
639 var oldAttributesMap = this._attributesMap || {};
640
641 this._attributes = [];
642 this._attributesMap = {};
643
644 for (var i = 0; i < attrs.length; i += 2) {
645 var name = attrs[i];
646 var value = attrs[i + 1];
647 this._addAttribute(name, value);
648
649 if (attributesChanged)
650 continue;
651
652 if (!oldAttributesMap[name] || oldAttributesMap[name].value !== valu e)
653 attributesChanged = true;
654 }
655 return attributesChanged;
656 },
657
658 /**
659 * @param {!WebInspector.DOMNode} prev
660 * @param {!DOMAgent.Node} payload
661 * @return {!WebInspector.DOMNode}
662 */
663 _insertChild: function(prev, payload)
664 {
665 var node = WebInspector.DOMNode.create(this._domModel, this.ownerDocumen t, this._isInShadowTree, payload);
666 this._children.splice(this._children.indexOf(prev) + 1, 0, node);
667 this._renumber();
668 return node;
669 },
670
671 /**
672 * @param {!WebInspector.DOMNode} node
673 */
674 _removeChild: function(node)
675 {
676 if (node.pseudoType()) {
677 this._pseudoElements.delete(node.pseudoType());
678 } else {
679 var shadowRootIndex = this._shadowRoots.indexOf(node);
680 if (shadowRootIndex !== -1) {
681 this._shadowRoots.splice(shadowRootIndex, 1);
682 } else {
683 console.assert(this._children.indexOf(node) !== -1);
684 this._children.splice(this._children.indexOf(node), 1);
685 }
686 }
687 node.parentNode = null;
688 this._subtreeMarkerCount -= node._subtreeMarkerCount;
689 if (node._subtreeMarkerCount)
690 this._domModel.dispatchEventToListeners(WebInspector.DOMModel.Events .MarkersChanged, this);
691 this._renumber();
692 },
693
694 /**
695 * @param {!Array.<!DOMAgent.Node>} payloads
696 */
697 _setChildrenPayload: function(payloads)
698 {
699 // We set children in the constructor.
700 if (this._contentDocument)
701 return;
702
703 this._children = [];
704 for (var i = 0; i < payloads.length; ++i) {
705 var payload = payloads[i];
706 var node = WebInspector.DOMNode.create(this._domModel, this.ownerDoc ument, this._isInShadowTree, payload);
707 this._children.push(node);
708 }
709 this._renumber();
710 },
711
712 /**
713 * @param {!Array.<!DOMAgent.Node>|undefined} payloads
714 */
715 _setPseudoElements: function(payloads)
716 {
717 this._pseudoElements = new Map();
718 if (!payloads)
719 return;
720
721 for (var i = 0; i < payloads.length; ++i) {
722 var node = WebInspector.DOMNode.create(this._domModel, this.ownerDoc ument, this._isInShadowTree, payloads[i]);
723 node.parentNode = this;
724 this._pseudoElements.set(node.pseudoType(), node);
725 }
726 },
727
728 /**
729 * @param {!Array.<!DOMAgent.BackendNode>} payloads
730 */
731 _setDistributedNodePayloads: function(payloads)
732 {
733 this._distributedNodes = [];
734 for (var payload of payloads)
735 this._distributedNodes.push(new WebInspector.DOMNodeShortcut(this._d omModel.target(), payload.backendNodeId, payload.nodeType, payload.nodeName));
736 },
737
738 _renumber: function()
739 {
740 this._childNodeCount = this._children.length;
741 if (this._childNodeCount === 0) {
742 this.firstChild = null;
743 this.lastChild = null;
744 return;
745 }
746 this.firstChild = this._children[0];
747 this.lastChild = this._children[this._childNodeCount - 1];
748 for (var i = 0; i < this._childNodeCount; ++i) {
749 var child = this._children[i];
750 child.index = i;
751 child.nextSibling = i + 1 < this._childNodeCount ? this._children[i + 1] : null;
752 child.previousSibling = i - 1 >= 0 ? this._children[i - 1] : null;
753 child.parentNode = this;
754 }
755 },
756
757 /**
758 * @param {string} name
759 * @param {string} value
760 */
761 _addAttribute: function(name, value)
762 {
763 var attr = {
764 name: name,
765 value: value,
766 _node: this
767 };
768 this._attributesMap[name] = attr;
769 this._attributes.push(attr);
770 },
771
772 /**
773 * @param {string} name
774 * @param {string} value
775 */
776 _setAttribute: function(name, value)
777 {
778 var attr = this._attributesMap[name];
779 if (attr)
780 attr.value = value;
781 else
782 this._addAttribute(name, value);
783 },
784
785 /**
786 * @param {string} name
787 */
788 _removeAttribute: function(name)
789 {
790 var attr = this._attributesMap[name];
791 if (attr) {
792 this._attributes.remove(attr);
793 delete this._attributesMap[name];
794 }
795 },
796
797 /**
798 * @param {!WebInspector.DOMNode} targetNode
799 * @param {?WebInspector.DOMNode} anchorNode
800 * @param {function(?Protocol.Error, !DOMAgent.NodeId=)=} callback
801 */
802 copyTo: function(targetNode, anchorNode, callback)
803 {
804 this._agent.copyTo(this.id, targetNode.id, anchorNode ? anchorNode.id : undefined, this._domModel._markRevision(this, callback));
805 },
806
807 /**
808 * @param {!WebInspector.DOMNode} targetNode
809 * @param {?WebInspector.DOMNode} anchorNode
810 * @param {function(?Protocol.Error, !DOMAgent.NodeId=)=} callback
811 */
812 moveTo: function(targetNode, anchorNode, callback)
813 {
814 this._agent.moveTo(this.id, targetNode.id, anchorNode ? anchorNode.id : undefined, this._domModel._markRevision(this, callback));
815 },
816
817 /**
818 * @return {boolean}
819 */
820 isXMLNode: function()
821 {
822 return !!this._xmlVersion;
823 },
824
825 /**
826 * @param {string} name
827 * @param {?*} value
828 */
829 setMarker: function(name, value)
830 {
831 if (value === null) {
832 if (!this._markers.has(name))
833 return;
834
835 this._markers.delete(name);
836 for (var node = this; node; node = node.parentNode)
837 --node._subtreeMarkerCount;
838 for (var node = this; node; node = node.parentNode)
839 this._domModel.dispatchEventToListeners(WebInspector.DOMModel.Ev ents.MarkersChanged, node);
840 return;
841 }
842
843 if (this.parentNode && !this._markers.has(name)) {
844 for (var node = this; node; node = node.parentNode)
845 ++node._subtreeMarkerCount;
846 }
847 this._markers.set(name, value);
848 for (var node = this; node; node = node.parentNode)
849 this._domModel.dispatchEventToListeners(WebInspector.DOMModel.Events .MarkersChanged, node);
850 },
851
852 /**
853 * @param {string} name
854 * @return {?T}
855 * @template T
856 */
857 marker: function(name)
858 {
859 return this._markers.get(name) || null;
860 },
861
862 /**
863 * @param {function(!WebInspector.DOMNode, string)} visitor
864 */
865 traverseMarkers: function(visitor)
866 {
867 /**
868 * @param {!WebInspector.DOMNode} node
869 */
870 function traverse(node)
871 {
872 if (!node._subtreeMarkerCount)
873 return;
874 for (var marker of node._markers.keys())
875 visitor(node, marker);
876 if (!node._children)
877 return;
878 for (var child of node._children)
879 traverse(child);
880 }
881 traverse(this);
882 },
883
884 /**
885 * @param {string} url
886 * @return {?string}
887 */
888 resolveURL: function(url)
889 {
890 if (!url)
891 return url;
892 for (var frameOwnerCandidate = this; frameOwnerCandidate; frameOwnerCand idate = frameOwnerCandidate.parentNode) {
893 if (frameOwnerCandidate.baseURL)
894 return WebInspector.ParsedURL.completeURL(frameOwnerCandidate.ba seURL, url);
895 }
896 return null;
897 },
898
899 /**
900 * @param {string=} mode
901 * @param {!RuntimeAgent.RemoteObjectId=} objectId
902 */
903 highlight: function(mode, objectId)
904 {
905 this._domModel.highlightDOMNode(this.id, mode, undefined, objectId);
906 },
907
908 highlightForTwoSeconds: function()
909 {
910 this._domModel.highlightDOMNodeForTwoSeconds(this.id);
911 },
912
913 /**
914 * @param {string=} objectGroup
915 * @param {function(?WebInspector.RemoteObject)=} callback
916 */
917 resolveToObject: function(objectGroup, callback)
918 {
919 this._agent.resolveNode(this.id, objectGroup, mycallback.bind(this));
920
921 /**
922 * @param {?Protocol.Error} error
923 * @param {!RuntimeAgent.RemoteObject} object
924 * @this {WebInspector.DOMNode}
925 */
926 function mycallback(error, object)
927 {
928 if (!callback)
929 return;
930
931 if (error || !object)
932 callback(null);
933 else
934 callback(this.target().runtimeModel.createRemoteObject(object));
935 }
936 },
937
938 /**
939 * @param {string=} objectGroup
940 * @return {!Promise<!WebInspector.RemoteObject>}
941 */
942 resolveToObjectPromise: function(objectGroup)
943 {
944 return new Promise(resolveToObject.bind(this));
945 /**
946 * @param {function(?)} fulfill
947 * @param {function(*)} reject
948 * @this {WebInspector.DOMNode}
949 */
950 function resolveToObject(fulfill, reject)
951 {
952 this.resolveToObject(objectGroup, mycallback);
953 function mycallback(object)
954 {
955 if (object)
956 fulfill(object);
957 else
958 reject(null);
959 }
960 }
961 },
962
963 /**
964 * @param {function(?DOMAgent.BoxModel)} callback
965 */
966 boxModel: function(callback)
967 {
968 this._agent.getBoxModel(this.id, this._domModel._wrapClientCallback(call back));
969 },
970
971 setAsInspectedNode: function()
972 {
973 var node = this;
974 while (true) {
975 var ancestor = node.ancestorUserAgentShadowRoot();
976 if (!ancestor)
977 break;
978 ancestor = node.ancestorShadowHost();
979 if (!ancestor)
980 break;
981 // User agent shadow root, keep climbing up.
982 node = ancestor;
983 }
984 this._agent.setInspectedNode(node.id);
985 },
986
987 /**
988 * @return {?WebInspector.DOMNode}
989 */
990 enclosingElementOrSelf: function()
991 {
992 var node = this;
993 if (node && node.nodeType() === Node.TEXT_NODE && node.parentNode)
994 node = node.parentNode;
995
996 if (node && node.nodeType() !== Node.ELEMENT_NODE)
997 node = null;
998 return node;
999 },
1000
1001 __proto__: WebInspector.SDKObject.prototype
1002 };
1003
1004 /** 928 /**
1005 * @param {!WebInspector.Target} target 929 * @unrestricted
1006 * @param {number} backendNodeId
1007 * @constructor
1008 */ 930 */
1009 WebInspector.DeferredDOMNode = function(target, backendNodeId) 931 WebInspector.DeferredDOMNode = class {
1010 { 932 /**
933 * @param {!WebInspector.Target} target
934 * @param {number} backendNodeId
935 */
936 constructor(target, backendNodeId) {
1011 this._domModel = WebInspector.DOMModel.fromTarget(target); 937 this._domModel = WebInspector.DOMModel.fromTarget(target);
1012 this._backendNodeId = backendNodeId; 938 this._backendNodeId = backendNodeId;
939 }
940
941 /**
942 * @param {function(?WebInspector.DOMNode)} callback
943 */
944 resolve(callback) {
945 if (!this._domModel) {
946 callback(null);
947 return;
948 }
949
950 this._domModel.pushNodesByBackendIdsToFrontend(new Set([this._backendNodeId] ), onGotNode.bind(this));
951
952 /**
953 * @param {?Map<number, ?WebInspector.DOMNode>} nodeIds
954 * @this {WebInspector.DeferredDOMNode}
955 */
956 function onGotNode(nodeIds) {
957 callback(nodeIds && (nodeIds.get(this._backendNodeId) || null));
958 }
959 }
960
961 /**
962 * @return {!Promise.<!WebInspector.DOMNode>}
963 */
964 resolvePromise() {
965 /**
966 * @param {function(?)} fulfill
967 * @param {function(*)} reject
968 * @this {WebInspector.DeferredDOMNode}
969 */
970 function resolveNode(fulfill, reject) {
971 /**
972 * @param {?WebInspector.DOMNode} node
973 */
974 function mycallback(node) {
975 fulfill(node);
976 }
977 this.resolve(mycallback);
978 }
979 return new Promise(resolveNode.bind(this));
980 }
981
982 /**
983 * @return {number}
984 */
985 backendNodeId() {
986 return this._backendNodeId;
987 }
988
989 highlight() {
990 if (this._domModel)
991 this._domModel.highlightDOMNode(undefined, undefined, this._backendNodeId) ;
992 }
1013 }; 993 };
1014 994
1015 WebInspector.DeferredDOMNode.prototype = {
1016 /**
1017 * @param {function(?WebInspector.DOMNode)} callback
1018 */
1019 resolve: function(callback)
1020 {
1021 if (!this._domModel) {
1022 callback(null);
1023 return;
1024 }
1025
1026 this._domModel.pushNodesByBackendIdsToFrontend(new Set([this._backendNod eId]), onGotNode.bind(this));
1027
1028 /**
1029 * @param {?Map<number, ?WebInspector.DOMNode>} nodeIds
1030 * @this {WebInspector.DeferredDOMNode}
1031 */
1032 function onGotNode(nodeIds)
1033 {
1034 callback(nodeIds && (nodeIds.get(this._backendNodeId) || null));
1035 }
1036 },
1037
1038 /**
1039 * @return {!Promise.<!WebInspector.DOMNode>}
1040 */
1041 resolvePromise: function()
1042 {
1043 /**
1044 * @param {function(?)} fulfill
1045 * @param {function(*)} reject
1046 * @this {WebInspector.DeferredDOMNode}
1047 */
1048 function resolveNode(fulfill, reject)
1049 {
1050 /**
1051 * @param {?WebInspector.DOMNode} node
1052 */
1053 function mycallback(node)
1054 {
1055 fulfill(node);
1056 }
1057 this.resolve(mycallback);
1058 }
1059 return new Promise(resolveNode.bind(this));
1060 },
1061
1062 /**
1063 * @return {number}
1064 */
1065 backendNodeId: function()
1066 {
1067 return this._backendNodeId;
1068 },
1069
1070 highlight: function()
1071 {
1072 if (this._domModel)
1073 this._domModel.highlightDOMNode(undefined, undefined, this._backendN odeId);
1074 }
1075 };
1076
1077 /** 995 /**
1078 * @constructor 996 * @unrestricted
1079 * @param {!WebInspector.Target} target
1080 * @param {number} backendNodeId
1081 * @param {number} nodeType
1082 * @param {string} nodeName
1083 */ 997 */
1084 WebInspector.DOMNodeShortcut = function(target, backendNodeId, nodeType, nodeNam e) 998 WebInspector.DOMNodeShortcut = class {
1085 { 999 /**
1000 * @param {!WebInspector.Target} target
1001 * @param {number} backendNodeId
1002 * @param {number} nodeType
1003 * @param {string} nodeName
1004 */
1005 constructor(target, backendNodeId, nodeType, nodeName) {
1086 this.nodeType = nodeType; 1006 this.nodeType = nodeType;
1087 this.nodeName = nodeName; 1007 this.nodeName = nodeName;
1088 this.deferredNode = new WebInspector.DeferredDOMNode(target, backendNodeId); 1008 this.deferredNode = new WebInspector.DeferredDOMNode(target, backendNodeId);
1009 }
1089 }; 1010 };
1090 1011
1091 /** 1012 /**
1092 * @extends {WebInspector.DOMNode} 1013 * @unrestricted
1093 * @constructor
1094 * @param {!WebInspector.DOMModel} domModel
1095 * @param {!DOMAgent.Node} payload
1096 */ 1014 */
1097 WebInspector.DOMDocument = function(domModel, payload) 1015 WebInspector.DOMDocument = class extends WebInspector.DOMNode {
1098 { 1016 /**
1099 WebInspector.DOMNode.call(this, domModel); 1017 * @param {!WebInspector.DOMModel} domModel
1018 * @param {!DOMAgent.Node} payload
1019 */
1020 constructor(domModel, payload) {
1021 super(domModel);
1100 this._init(this, false, payload); 1022 this._init(this, false, payload);
1101 this.documentURL = payload.documentURL || ""; 1023 this.documentURL = payload.documentURL || '';
1102 this.baseURL = payload.baseURL || ""; 1024 this.baseURL = payload.baseURL || '';
1103 this._listeners = {}; 1025 this._listeners = {};
1026 }
1104 }; 1027 };
1105 1028
1106 WebInspector.DOMDocument.prototype = {
1107 __proto__: WebInspector.DOMNode.prototype
1108 };
1109
1110 /** 1029 /**
1111 * @constructor 1030 * @unrestricted
1112 * @extends {WebInspector.SDKModel}
1113 * @param {!WebInspector.Target} target
1114 */ 1031 */
1115 WebInspector.DOMModel = function(target) { 1032 WebInspector.DOMModel = class extends WebInspector.SDKModel {
1116 WebInspector.SDKModel.call(this, WebInspector.DOMModel, target); 1033 /**
1034 * @param {!WebInspector.Target} target
1035 */
1036 constructor(target) {
1037 super(WebInspector.DOMModel, target);
1117 1038
1118 this._agent = target.domAgent(); 1039 this._agent = target.domAgent();
1119 1040
1120 /** @type {!Object.<number, !WebInspector.DOMNode>} */ 1041 /** @type {!Object.<number, !WebInspector.DOMNode>} */
1121 this._idToDOMNode = {}; 1042 this._idToDOMNode = {};
1122 /** @type {?WebInspector.DOMDocument} */ 1043 /** @type {?WebInspector.DOMDocument} */
1123 this._document = null; 1044 this._document = null;
1124 /** @type {!Object.<number, boolean>} */ 1045 /** @type {!Object.<number, boolean>} */
1125 this._attributeLoadNodeIds = {}; 1046 this._attributeLoadNodeIds = {};
1126 target.registerDOMDispatcher(new WebInspector.DOMDispatcher(this)); 1047 target.registerDOMDispatcher(new WebInspector.DOMDispatcher(this));
1127 1048
1128 this._inspectModeEnabled = false; 1049 this._inspectModeEnabled = false;
1129 1050
1130 this._defaultHighlighter = new WebInspector.DefaultDOMNodeHighlighter(this._ agent); 1051 this._defaultHighlighter = new WebInspector.DefaultDOMNodeHighlighter(this._ agent);
1131 this._highlighter = this._defaultHighlighter; 1052 this._highlighter = this._defaultHighlighter;
1132 1053
1133 this._agent.enable(); 1054 this._agent.enable();
1055 }
1056
1057 /**
1058 * @param {!WebInspector.RemoteObject} object
1059 */
1060 static highlightObjectAsDOMNode(object) {
1061 var domModel = WebInspector.DOMModel.fromTarget(object.target());
1062 if (domModel)
1063 domModel.highlightDOMNode(undefined, undefined, undefined, object.objectId );
1064 }
1065
1066 /**
1067 * @return {!Array<!WebInspector.DOMModel>}
1068 */
1069 static instances() {
1070 var result = [];
1071 for (var target of WebInspector.targetManager.targets()) {
1072 var domModel = WebInspector.DOMModel.fromTarget(target);
1073 if (domModel)
1074 result.push(domModel);
1075 }
1076 return result;
1077 }
1078
1079 static hideDOMNodeHighlight() {
1080 for (var domModel of WebInspector.DOMModel.instances())
1081 domModel.highlightDOMNode(0);
1082 }
1083
1084 static muteHighlight() {
1085 WebInspector.DOMModel.hideDOMNodeHighlight();
1086 WebInspector.DOMModel._highlightDisabled = true;
1087 }
1088
1089 static unmuteHighlight() {
1090 WebInspector.DOMModel._highlightDisabled = false;
1091 }
1092
1093 static cancelSearch() {
1094 for (var domModel of WebInspector.DOMModel.instances())
1095 domModel._cancelSearch();
1096 }
1097
1098 /**
1099 * @param {!WebInspector.Target} target
1100 * @return {?WebInspector.DOMModel}
1101 */
1102 static fromTarget(target) {
1103 return /** @type {?WebInspector.DOMModel} */ (target.model(WebInspector.DOMM odel));
1104 }
1105
1106 /**
1107 * @param {!WebInspector.DOMNode} node
1108 */
1109 _scheduleMutationEvent(node) {
1110 if (!this.hasEventListeners(WebInspector.DOMModel.Events.DOMMutated))
1111 return;
1112
1113 this._lastMutationId = (this._lastMutationId || 0) + 1;
1114 Promise.resolve().then(callObserve.bind(this, node, this._lastMutationId));
1115
1116 /**
1117 * @this {WebInspector.DOMModel}
1118 * @param {!WebInspector.DOMNode} node
1119 * @param {number} mutationId
1120 */
1121 function callObserve(node, mutationId) {
1122 if (!this.hasEventListeners(WebInspector.DOMModel.Events.DOMMutated) || th is._lastMutationId !== mutationId)
1123 return;
1124
1125 this.dispatchEventToListeners(WebInspector.DOMModel.Events.DOMMutated, nod e);
1126 }
1127 }
1128
1129 /**
1130 * @param {function(!WebInspector.DOMDocument)=} callback
1131 */
1132 requestDocument(callback) {
1133 if (this._document) {
1134 if (callback)
1135 callback(this._document);
1136 return;
1137 }
1138
1139 if (this._pendingDocumentRequestCallbacks) {
1140 this._pendingDocumentRequestCallbacks.push(callback);
1141 return;
1142 }
1143
1144 this._pendingDocumentRequestCallbacks = [callback];
1145
1146 /**
1147 * @this {WebInspector.DOMModel}
1148 * @param {?Protocol.Error} error
1149 * @param {!DOMAgent.Node} root
1150 */
1151 function onDocumentAvailable(error, root) {
1152 if (!error)
1153 this._setDocument(root);
1154
1155 for (var i = 0; i < this._pendingDocumentRequestCallbacks.length; ++i) {
1156 var callback = this._pendingDocumentRequestCallbacks[i];
1157 if (callback)
1158 callback(this._document);
1159 }
1160 delete this._pendingDocumentRequestCallbacks;
1161 }
1162
1163 this._agent.getDocument(undefined, undefined, onDocumentAvailable.bind(this) );
1164 }
1165
1166 /**
1167 * @return {?WebInspector.DOMDocument}
1168 */
1169 existingDocument() {
1170 return this._document;
1171 }
1172
1173 /**
1174 * @param {!RuntimeAgent.RemoteObjectId} objectId
1175 * @param {function(?WebInspector.DOMNode)=} callback
1176 */
1177 pushNodeToFrontend(objectId, callback) {
1178 /**
1179 * @param {?DOMAgent.NodeId} nodeId
1180 * @this {!WebInspector.DOMModel}
1181 */
1182 function mycallback(nodeId) {
1183 callback(nodeId ? this.nodeForId(nodeId) : null);
1184 }
1185 this._dispatchWhenDocumentAvailable(this._agent.requestNode.bind(this._agent , objectId), mycallback.bind(this));
1186 }
1187
1188 /**
1189 * @param {string} path
1190 * @param {function(?number)=} callback
1191 */
1192 pushNodeByPathToFrontend(path, callback) {
1193 this._dispatchWhenDocumentAvailable(this._agent.pushNodeByPathToFrontend.bin d(this._agent, path), callback);
1194 }
1195
1196 /**
1197 * @param {!Set<number>} backendNodeIds
1198 * @param {function(?Map<number, ?WebInspector.DOMNode>)} callback
1199 */
1200 pushNodesByBackendIdsToFrontend(backendNodeIds, callback) {
1201 var backendNodeIdsArray = backendNodeIds.valuesArray();
1202 /**
1203 * @param {?Array<!DOMAgent.NodeId>} nodeIds
1204 * @this {!WebInspector.DOMModel}
1205 */
1206 function mycallback(nodeIds) {
1207 if (!nodeIds) {
1208 callback(null);
1209 return;
1210 }
1211 /** @type {!Map<number, ?WebInspector.DOMNode>} */
1212 var map = new Map();
1213 for (var i = 0; i < nodeIds.length; ++i) {
1214 if (nodeIds[i])
1215 map.set(backendNodeIdsArray[i], this.nodeForId(nodeIds[i]));
1216 }
1217 callback(map);
1218 }
1219 this._dispatchWhenDocumentAvailable(
1220 this._agent.pushNodesByBackendIdsToFrontend.bind(this._agent, backendNod eIdsArray), mycallback.bind(this));
1221 }
1222
1223 /**
1224 * @param {function(!T)=} callback
1225 * @return {function(?Protocol.Error, !T=)|undefined}
1226 * @template T
1227 */
1228 _wrapClientCallback(callback) {
1229 if (!callback)
1230 return;
1231 /**
1232 * @param {?Protocol.Error} error
1233 * @param {!T=} result
1234 * @template T
1235 */
1236 var wrapper = function(error, result) {
1237 // Caller is responsible for handling the actual error.
1238 callback(error ? null : result);
1239 };
1240 return wrapper;
1241 }
1242
1243 /**
1244 * @param {function(function(?Protocol.Error, !T=)=)} func
1245 * @param {function(!T)=} callback
1246 * @template T
1247 */
1248 _dispatchWhenDocumentAvailable(func, callback) {
1249 var callbackWrapper = this._wrapClientCallback(callback);
1250
1251 /**
1252 * @this {WebInspector.DOMModel}
1253 */
1254 function onDocumentAvailable() {
1255 if (this._document)
1256 func(callbackWrapper);
1257 else {
1258 if (callbackWrapper)
1259 callbackWrapper('No document');
1260 }
1261 }
1262 this.requestDocument(onDocumentAvailable.bind(this));
1263 }
1264
1265 /**
1266 * @param {!DOMAgent.NodeId} nodeId
1267 * @param {string} name
1268 * @param {string} value
1269 */
1270 _attributeModified(nodeId, name, value) {
1271 var node = this._idToDOMNode[nodeId];
1272 if (!node)
1273 return;
1274
1275 node._setAttribute(name, value);
1276 this.dispatchEventToListeners(WebInspector.DOMModel.Events.AttrModified, {no de: node, name: name});
1277 this._scheduleMutationEvent(node);
1278 }
1279
1280 /**
1281 * @param {!DOMAgent.NodeId} nodeId
1282 * @param {string} name
1283 */
1284 _attributeRemoved(nodeId, name) {
1285 var node = this._idToDOMNode[nodeId];
1286 if (!node)
1287 return;
1288 node._removeAttribute(name);
1289 this.dispatchEventToListeners(WebInspector.DOMModel.Events.AttrRemoved, {nod e: node, name: name});
1290 this._scheduleMutationEvent(node);
1291 }
1292
1293 /**
1294 * @param {!Array.<!DOMAgent.NodeId>} nodeIds
1295 */
1296 _inlineStyleInvalidated(nodeIds) {
1297 for (var i = 0; i < nodeIds.length; ++i)
1298 this._attributeLoadNodeIds[nodeIds[i]] = true;
1299 if ('_loadNodeAttributesTimeout' in this)
1300 return;
1301 this._loadNodeAttributesTimeout = setTimeout(this._loadNodeAttributes.bind(t his), 20);
1302 }
1303
1304 _loadNodeAttributes() {
1305 /**
1306 * @this {WebInspector.DOMModel}
1307 * @param {!DOMAgent.NodeId} nodeId
1308 * @param {?Protocol.Error} error
1309 * @param {!Array.<string>} attributes
1310 */
1311 function callback(nodeId, error, attributes) {
1312 if (error) {
1313 // We are calling _loadNodeAttributes asynchronously, it is ok if node i s not found.
1314 return;
1315 }
1316 var node = this._idToDOMNode[nodeId];
1317 if (node) {
1318 if (node._setAttributesPayload(attributes)) {
1319 this.dispatchEventToListeners(WebInspector.DOMModel.Events.AttrModifie d, {node: node, name: 'style'});
1320 this._scheduleMutationEvent(node);
1321 }
1322 }
1323 }
1324
1325 delete this._loadNodeAttributesTimeout;
1326
1327 for (var nodeId in this._attributeLoadNodeIds) {
1328 var nodeIdAsNumber = parseInt(nodeId, 10);
1329 this._agent.getAttributes(nodeIdAsNumber, callback.bind(this, nodeIdAsNumb er));
1330 }
1331 this._attributeLoadNodeIds = {};
1332 }
1333
1334 /**
1335 * @param {!DOMAgent.NodeId} nodeId
1336 * @param {string} newValue
1337 */
1338 _characterDataModified(nodeId, newValue) {
1339 var node = this._idToDOMNode[nodeId];
1340 node._nodeValue = newValue;
1341 this.dispatchEventToListeners(WebInspector.DOMModel.Events.CharacterDataModi fied, node);
1342 this._scheduleMutationEvent(node);
1343 }
1344
1345 /**
1346 * @param {!DOMAgent.NodeId} nodeId
1347 * @return {?WebInspector.DOMNode}
1348 */
1349 nodeForId(nodeId) {
1350 return this._idToDOMNode[nodeId] || null;
1351 }
1352
1353 _documentUpdated() {
1354 this._setDocument(null);
1355 }
1356
1357 /**
1358 * @param {?DOMAgent.Node} payload
1359 */
1360 _setDocument(payload) {
1361 this._idToDOMNode = {};
1362 if (payload && 'nodeId' in payload)
1363 this._document = new WebInspector.DOMDocument(this, payload);
1364 else
1365 this._document = null;
1366 this.dispatchEventToListeners(WebInspector.DOMModel.Events.DocumentUpdated, this._document);
1367 }
1368
1369 /**
1370 * @param {!DOMAgent.Node} payload
1371 */
1372 _setDetachedRoot(payload) {
1373 if (payload.nodeName === '#document')
1374 new WebInspector.DOMDocument(this, payload);
1375 else
1376 WebInspector.DOMNode.create(this, null, false, payload);
1377 }
1378
1379 /**
1380 * @param {!DOMAgent.NodeId} parentId
1381 * @param {!Array.<!DOMAgent.Node>} payloads
1382 */
1383 _setChildNodes(parentId, payloads) {
1384 if (!parentId && payloads.length) {
1385 this._setDetachedRoot(payloads[0]);
1386 return;
1387 }
1388
1389 var parent = this._idToDOMNode[parentId];
1390 parent._setChildrenPayload(payloads);
1391 }
1392
1393 /**
1394 * @param {!DOMAgent.NodeId} nodeId
1395 * @param {number} newValue
1396 */
1397 _childNodeCountUpdated(nodeId, newValue) {
1398 var node = this._idToDOMNode[nodeId];
1399 node._childNodeCount = newValue;
1400 this.dispatchEventToListeners(WebInspector.DOMModel.Events.ChildNodeCountUpd ated, node);
1401 this._scheduleMutationEvent(node);
1402 }
1403
1404 /**
1405 * @param {!DOMAgent.NodeId} parentId
1406 * @param {!DOMAgent.NodeId} prevId
1407 * @param {!DOMAgent.Node} payload
1408 */
1409 _childNodeInserted(parentId, prevId, payload) {
1410 var parent = this._idToDOMNode[parentId];
1411 var prev = this._idToDOMNode[prevId];
1412 var node = parent._insertChild(prev, payload);
1413 this._idToDOMNode[node.id] = node;
1414 this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeInserted, nod e);
1415 this._scheduleMutationEvent(node);
1416 }
1417
1418 /**
1419 * @param {!DOMAgent.NodeId} parentId
1420 * @param {!DOMAgent.NodeId} nodeId
1421 */
1422 _childNodeRemoved(parentId, nodeId) {
1423 var parent = this._idToDOMNode[parentId];
1424 var node = this._idToDOMNode[nodeId];
1425 parent._removeChild(node);
1426 this._unbind(node);
1427 this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeRemoved, {nod e: node, parent: parent});
1428 this._scheduleMutationEvent(node);
1429 }
1430
1431 /**
1432 * @param {!DOMAgent.NodeId} hostId
1433 * @param {!DOMAgent.Node} root
1434 */
1435 _shadowRootPushed(hostId, root) {
1436 var host = this._idToDOMNode[hostId];
1437 if (!host)
1438 return;
1439 var node = WebInspector.DOMNode.create(this, host.ownerDocument, true, root) ;
1440 node.parentNode = host;
1441 this._idToDOMNode[node.id] = node;
1442 host._shadowRoots.unshift(node);
1443 this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeInserted, nod e);
1444 this._scheduleMutationEvent(node);
1445 }
1446
1447 /**
1448 * @param {!DOMAgent.NodeId} hostId
1449 * @param {!DOMAgent.NodeId} rootId
1450 */
1451 _shadowRootPopped(hostId, rootId) {
1452 var host = this._idToDOMNode[hostId];
1453 if (!host)
1454 return;
1455 var root = this._idToDOMNode[rootId];
1456 if (!root)
1457 return;
1458 host._removeChild(root);
1459 this._unbind(root);
1460 this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeRemoved, {nod e: root, parent: host});
1461 this._scheduleMutationEvent(root);
1462 }
1463
1464 /**
1465 * @param {!DOMAgent.NodeId} parentId
1466 * @param {!DOMAgent.Node} pseudoElement
1467 */
1468 _pseudoElementAdded(parentId, pseudoElement) {
1469 var parent = this._idToDOMNode[parentId];
1470 if (!parent)
1471 return;
1472 var node = WebInspector.DOMNode.create(this, parent.ownerDocument, false, ps eudoElement);
1473 node.parentNode = parent;
1474 this._idToDOMNode[node.id] = node;
1475 console.assert(!parent._pseudoElements.get(node.pseudoType()));
1476 parent._pseudoElements.set(node.pseudoType(), node);
1477 this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeInserted, nod e);
1478 this._scheduleMutationEvent(node);
1479 }
1480
1481 /**
1482 * @param {!DOMAgent.NodeId} parentId
1483 * @param {!DOMAgent.NodeId} pseudoElementId
1484 */
1485 _pseudoElementRemoved(parentId, pseudoElementId) {
1486 var parent = this._idToDOMNode[parentId];
1487 if (!parent)
1488 return;
1489 var pseudoElement = this._idToDOMNode[pseudoElementId];
1490 if (!pseudoElement)
1491 return;
1492 parent._removeChild(pseudoElement);
1493 this._unbind(pseudoElement);
1494 this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeRemoved, {nod e: pseudoElement, parent: parent});
1495 this._scheduleMutationEvent(pseudoElement);
1496 }
1497
1498 /**
1499 * @param {!DOMAgent.NodeId} insertionPointId
1500 * @param {!Array.<!DOMAgent.BackendNode>} distributedNodes
1501 */
1502 _distributedNodesUpdated(insertionPointId, distributedNodes) {
1503 var insertionPoint = this._idToDOMNode[insertionPointId];
1504 if (!insertionPoint)
1505 return;
1506 insertionPoint._setDistributedNodePayloads(distributedNodes);
1507 this.dispatchEventToListeners(WebInspector.DOMModel.Events.DistributedNodesC hanged, insertionPoint);
1508 this._scheduleMutationEvent(insertionPoint);
1509 }
1510
1511 /**
1512 * @param {!WebInspector.DOMNode} node
1513 */
1514 _unbind(node) {
1515 delete this._idToDOMNode[node.id];
1516 for (var i = 0; node._children && i < node._children.length; ++i)
1517 this._unbind(node._children[i]);
1518 for (var i = 0; i < node._shadowRoots.length; ++i)
1519 this._unbind(node._shadowRoots[i]);
1520 var pseudoElements = node.pseudoElements();
1521 for (var value of pseudoElements.values())
1522 this._unbind(value);
1523 if (node._templateContent)
1524 this._unbind(node._templateContent);
1525 }
1526
1527 /**
1528 * @param {!DOMAgent.BackendNodeId} backendNodeId
1529 */
1530 _inspectNodeRequested(backendNodeId) {
1531 var deferredNode = new WebInspector.DeferredDOMNode(this.target(), backendNo deId);
1532 this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeInspected, de ferredNode);
1533 }
1534
1535 /**
1536 * @param {string} query
1537 * @param {boolean} includeUserAgentShadowDOM
1538 * @param {function(number)} searchCallback
1539 */
1540 performSearch(query, includeUserAgentShadowDOM, searchCallback) {
1541 WebInspector.DOMModel.cancelSearch();
1542
1543 /**
1544 * @param {?Protocol.Error} error
1545 * @param {string} searchId
1546 * @param {number} resultsCount
1547 * @this {WebInspector.DOMModel}
1548 */
1549 function callback(error, searchId, resultsCount) {
1550 this._searchId = searchId;
1551 searchCallback(resultsCount);
1552 }
1553 this._agent.performSearch(query, includeUserAgentShadowDOM, callback.bind(th is));
1554 }
1555
1556 /**
1557 * @param {string} query
1558 * @param {boolean} includeUserAgentShadowDOM
1559 * @return {!Promise.<number>}
1560 */
1561 performSearchPromise(query, includeUserAgentShadowDOM) {
1562 return new Promise(performSearch.bind(this));
1563
1564 /**
1565 * @param {function(number)} resolve
1566 * @this {WebInspector.DOMModel}
1567 */
1568 function performSearch(resolve) {
1569 this._agent.performSearch(query, includeUserAgentShadowDOM, callback.bind( this));
1570
1571 /**
1572 * @param {?Protocol.Error} error
1573 * @param {string} searchId
1574 * @param {number} resultsCount
1575 * @this {WebInspector.DOMModel}
1576 */
1577 function callback(error, searchId, resultsCount) {
1578 if (!error)
1579 this._searchId = searchId;
1580 resolve(error ? 0 : resultsCount);
1581 }
1582 }
1583 }
1584
1585 /**
1586 * @param {number} index
1587 * @param {?function(?WebInspector.DOMNode)} callback
1588 */
1589 searchResult(index, callback) {
1590 if (this._searchId)
1591 this._agent.getSearchResults(this._searchId, index, index + 1, searchResul tsCallback.bind(this));
1592 else
1593 callback(null);
1594
1595 /**
1596 * @param {?Protocol.Error} error
1597 * @param {!Array.<number>} nodeIds
1598 * @this {WebInspector.DOMModel}
1599 */
1600 function searchResultsCallback(error, nodeIds) {
1601 if (error) {
1602 console.error(error);
1603 callback(null);
1604 return;
1605 }
1606 if (nodeIds.length !== 1)
1607 return;
1608
1609 callback(this.nodeForId(nodeIds[0]));
1610 }
1611 }
1612
1613 _cancelSearch() {
1614 if (this._searchId) {
1615 this._agent.discardSearchResults(this._searchId);
1616 delete this._searchId;
1617 }
1618 }
1619
1620 /**
1621 * @param {!DOMAgent.NodeId} nodeId
1622 * @return {!Promise<!Array<string>>}
1623 */
1624 classNamesPromise(nodeId) {
1625 return new Promise(promiseBody.bind(this));
1626
1627 /**
1628 * @param {function(!Array<string>)} fulfill
1629 * @this {WebInspector.DOMModel}
1630 */
1631 function promiseBody(fulfill) {
1632 this._agent.collectClassNamesFromSubtree(nodeId, classNamesCallback);
1633
1634 /**
1635 * @param {?string} error
1636 * @param {?Array<string>} classNames
1637 */
1638 function classNamesCallback(error, classNames) {
1639 if (!error && classNames)
1640 fulfill(classNames);
1641 else
1642 fulfill([]);
1643 }
1644 }
1645 }
1646
1647 /**
1648 * @param {!DOMAgent.NodeId} nodeId
1649 * @param {string} selectors
1650 * @param {function(?DOMAgent.NodeId)=} callback
1651 */
1652 querySelector(nodeId, selectors, callback) {
1653 this._agent.querySelector(nodeId, selectors, this._wrapClientCallback(callba ck));
1654 }
1655
1656 /**
1657 * @param {!DOMAgent.NodeId} nodeId
1658 * @param {string} selectors
1659 * @param {function(!Array.<!DOMAgent.NodeId>=)=} callback
1660 */
1661 querySelectorAll(nodeId, selectors, callback) {
1662 this._agent.querySelectorAll(nodeId, selectors, this._wrapClientCallback(cal lback));
1663 }
1664
1665 /**
1666 * @param {!DOMAgent.NodeId=} nodeId
1667 * @param {string=} mode
1668 * @param {!DOMAgent.BackendNodeId=} backendNodeId
1669 * @param {!RuntimeAgent.RemoteObjectId=} objectId
1670 */
1671 highlightDOMNode(nodeId, mode, backendNodeId, objectId) {
1672 this.highlightDOMNodeWithConfig(nodeId, {mode: mode}, backendNodeId, objectI d);
1673 }
1674
1675 /**
1676 * @param {!DOMAgent.NodeId=} nodeId
1677 * @param {!{mode: (string|undefined), showInfo: (boolean|undefined), selector s: (string|undefined)}=} config
1678 * @param {!DOMAgent.BackendNodeId=} backendNodeId
1679 * @param {!RuntimeAgent.RemoteObjectId=} objectId
1680 */
1681 highlightDOMNodeWithConfig(nodeId, config, backendNodeId, objectId) {
1682 if (WebInspector.DOMModel._highlightDisabled)
1683 return;
1684 config = config || {mode: 'all', showInfo: undefined, selectors: undefined};
1685 if (this._hideDOMNodeHighlightTimeout) {
1686 clearTimeout(this._hideDOMNodeHighlightTimeout);
1687 delete this._hideDOMNodeHighlightTimeout;
1688 }
1689 var highlightConfig = this._buildHighlightConfig(config.mode);
1690 if (typeof config.showInfo !== 'undefined')
1691 highlightConfig.showInfo = config.showInfo;
1692 if (typeof config.selectors !== 'undefined')
1693 highlightConfig.selectorList = config.selectors;
1694 this._highlighter.highlightDOMNode(this.nodeForId(nodeId || 0), highlightCon fig, backendNodeId, objectId);
1695 }
1696
1697 /**
1698 * @param {!DOMAgent.NodeId} nodeId
1699 */
1700 highlightDOMNodeForTwoSeconds(nodeId) {
1701 this.highlightDOMNode(nodeId);
1702 this._hideDOMNodeHighlightTimeout =
1703 setTimeout(WebInspector.DOMModel.hideDOMNodeHighlight.bind(WebInspector. DOMModel), 2000);
1704 }
1705
1706 /**
1707 * @param {!PageAgent.FrameId} frameId
1708 */
1709 highlightFrame(frameId) {
1710 if (WebInspector.DOMModel._highlightDisabled)
1711 return;
1712 this._highlighter.highlightFrame(frameId);
1713 }
1714
1715 /**
1716 * @param {!DOMAgent.InspectMode} mode
1717 * @param {function(?Protocol.Error)=} callback
1718 */
1719 setInspectMode(mode, callback) {
1720 /**
1721 * @this {WebInspector.DOMModel}
1722 */
1723 function onDocumentAvailable() {
1724 this._inspectModeEnabled = mode !== DOMAgent.InspectMode.None;
1725 this.dispatchEventToListeners(WebInspector.DOMModel.Events.InspectModeWill BeToggled, this._inspectModeEnabled);
1726 this._highlighter.setInspectMode(mode, this._buildHighlightConfig(), callb ack);
1727 }
1728 this.requestDocument(onDocumentAvailable.bind(this));
1729 }
1730
1731 /**
1732 * @return {boolean}
1733 */
1734 inspectModeEnabled() {
1735 return this._inspectModeEnabled;
1736 }
1737
1738 /**
1739 * @param {string=} mode
1740 * @return {!DOMAgent.HighlightConfig}
1741 */
1742 _buildHighlightConfig(mode) {
1743 mode = mode || 'all';
1744 var showRulers = WebInspector.moduleSetting('showMetricsRulers').get();
1745 var highlightConfig = {showInfo: mode === 'all', showRulers: showRulers, sho wExtensionLines: showRulers};
1746 if (mode === 'all' || mode === 'content')
1747 highlightConfig.contentColor = WebInspector.Color.PageHighlight.Content.to ProtocolRGBA();
1748
1749 if (mode === 'all' || mode === 'padding')
1750 highlightConfig.paddingColor = WebInspector.Color.PageHighlight.Padding.to ProtocolRGBA();
1751
1752 if (mode === 'all' || mode === 'border')
1753 highlightConfig.borderColor = WebInspector.Color.PageHighlight.Border.toPr otocolRGBA();
1754
1755 if (mode === 'all' || mode === 'margin')
1756 highlightConfig.marginColor = WebInspector.Color.PageHighlight.Margin.toPr otocolRGBA();
1757
1758 if (mode === 'all') {
1759 highlightConfig.eventTargetColor = WebInspector.Color.PageHighlight.EventT arget.toProtocolRGBA();
1760 highlightConfig.shapeColor = WebInspector.Color.PageHighlight.Shape.toProt ocolRGBA();
1761 highlightConfig.shapeMarginColor = WebInspector.Color.PageHighlight.ShapeM argin.toProtocolRGBA();
1762 highlightConfig.displayAsMaterial = Runtime.experiments.isEnabled('inspect Tooltip');
1763 }
1764 return highlightConfig;
1765 }
1766
1767 /**
1768 * @param {!WebInspector.DOMNode} node
1769 * @param {function(?Protocol.Error, ...)=} callback
1770 * @return {function(...)}
1771 * @template T
1772 */
1773 _markRevision(node, callback) {
1774 /**
1775 * @param {?Protocol.Error} error
1776 * @this {WebInspector.DOMModel}
1777 */
1778 function wrapperFunction(error) {
1779 if (!error)
1780 this.markUndoableState();
1781
1782 if (callback)
1783 callback.apply(this, arguments);
1784 }
1785 return wrapperFunction.bind(this);
1786 }
1787
1788 markUndoableState() {
1789 this._agent.markUndoableState();
1790 }
1791
1792 /**
1793 * @param {function(?Protocol.Error)=} callback
1794 */
1795 undo(callback) {
1796 /**
1797 * @param {?Protocol.Error} error
1798 * @this {WebInspector.DOMModel}
1799 */
1800 function mycallback(error) {
1801 this.dispatchEventToListeners(WebInspector.DOMModel.Events.UndoRedoComplet ed);
1802 callback(error);
1803 }
1804
1805 this.dispatchEventToListeners(WebInspector.DOMModel.Events.UndoRedoRequested );
1806 this._agent.undo(callback);
1807 }
1808
1809 /**
1810 * @param {function(?Protocol.Error)=} callback
1811 */
1812 redo(callback) {
1813 /**
1814 * @param {?Protocol.Error} error
1815 * @this {WebInspector.DOMModel}
1816 */
1817 function mycallback(error) {
1818 this.dispatchEventToListeners(WebInspector.DOMModel.Events.UndoRedoComplet ed);
1819 callback(error);
1820 }
1821
1822 this.dispatchEventToListeners(WebInspector.DOMModel.Events.UndoRedoRequested );
1823 this._agent.redo(callback);
1824 }
1825
1826 /**
1827 * @param {?WebInspector.DOMNodeHighlighter} highlighter
1828 */
1829 setHighlighter(highlighter) {
1830 this._highlighter = highlighter || this._defaultHighlighter;
1831 }
1832
1833 /**
1834 * @param {number} x
1835 * @param {number} y
1836 * @param {function(?WebInspector.DOMNode)} callback
1837 */
1838 nodeForLocation(x, y, callback) {
1839 this._agent.getNodeForLocation(x, y, mycallback.bind(this));
1840
1841 /**
1842 * @param {?Protocol.Error} error
1843 * @param {number} nodeId
1844 * @this {WebInspector.DOMModel}
1845 */
1846 function mycallback(error, nodeId) {
1847 if (error) {
1848 callback(null);
1849 return;
1850 }
1851 callback(this.nodeForId(nodeId));
1852 }
1853 }
1854
1855 /**
1856 * @param {!WebInspector.RemoteObject} object
1857 * @param {function(?WebInspector.DOMNode)} callback
1858 */
1859 pushObjectAsNodeToFrontend(object, callback) {
1860 if (object.isNode())
1861 this.pushNodeToFrontend(object.objectId, callback);
1862 else
1863 callback(null);
1864 }
1865
1866 /**
1867 * @override
1868 * @return {!Promise}
1869 */
1870 suspendModel() {
1871 return new Promise(promiseBody.bind(this));
1872
1873 /**
1874 * @param {function()} fulfill
1875 * @this {WebInspector.DOMModel}
1876 */
1877 function promiseBody(fulfill) {
1878 this._agent.disable(callback.bind(this));
1879
1880 /**
1881 * @this {WebInspector.DOMModel}
1882 */
1883 function callback() {
1884 this._setDocument(null);
1885 fulfill();
1886 }
1887 }
1888 }
1889
1890 /**
1891 * @override
1892 * @return {!Promise}
1893 */
1894 resumeModel() {
1895 return new Promise(promiseBody.bind(this));
1896
1897 /**
1898 * @param {function()} fulfill
1899 * @this {WebInspector.DOMModel}
1900 */
1901 function promiseBody(fulfill) {
1902 this._agent.enable(fulfill);
1903 }
1904 }
1905
1906 /**
1907 * @param {!DOMAgent.NodeId} nodeId
1908 */
1909 nodeHighlightRequested(nodeId) {
1910 var node = this.nodeForId(nodeId);
1911 if (!node)
1912 return;
1913
1914 this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeHighlightedIn Overlay, node);
1915 }
1134 }; 1916 };
1135 1917
1136 /** @enum {symbol} */ 1918 /** @enum {symbol} */
1137 WebInspector.DOMModel.Events = { 1919 WebInspector.DOMModel.Events = {
1138 AttrModified: Symbol("AttrModified"), 1920 AttrModified: Symbol('AttrModified'),
1139 AttrRemoved: Symbol("AttrRemoved"), 1921 AttrRemoved: Symbol('AttrRemoved'),
1140 CharacterDataModified: Symbol("CharacterDataModified"), 1922 CharacterDataModified: Symbol('CharacterDataModified'),
1141 DOMMutated: Symbol("DOMMutated"), 1923 DOMMutated: Symbol('DOMMutated'),
1142 NodeInserted: Symbol("NodeInserted"), 1924 NodeInserted: Symbol('NodeInserted'),
1143 NodeInspected: Symbol("NodeInspected"), 1925 NodeInspected: Symbol('NodeInspected'),
1144 NodeHighlightedInOverlay: Symbol("NodeHighlightedInOverlay"), 1926 NodeHighlightedInOverlay: Symbol('NodeHighlightedInOverlay'),
1145 NodeRemoved: Symbol("NodeRemoved"), 1927 NodeRemoved: Symbol('NodeRemoved'),
1146 DocumentUpdated: Symbol("DocumentUpdated"), 1928 DocumentUpdated: Symbol('DocumentUpdated'),
1147 ChildNodeCountUpdated: Symbol("ChildNodeCountUpdated"), 1929 ChildNodeCountUpdated: Symbol('ChildNodeCountUpdated'),
1148 UndoRedoRequested: Symbol("UndoRedoRequested"), 1930 UndoRedoRequested: Symbol('UndoRedoRequested'),
1149 UndoRedoCompleted: Symbol("UndoRedoCompleted"), 1931 UndoRedoCompleted: Symbol('UndoRedoCompleted'),
1150 DistributedNodesChanged: Symbol("DistributedNodesChanged"), 1932 DistributedNodesChanged: Symbol('DistributedNodesChanged'),
1151 ModelSuspended: Symbol("ModelSuspended"), 1933 ModelSuspended: Symbol('ModelSuspended'),
1152 InspectModeWillBeToggled: Symbol("InspectModeWillBeToggled"), 1934 InspectModeWillBeToggled: Symbol('InspectModeWillBeToggled'),
1153 MarkersChanged: Symbol("MarkersChanged") 1935 MarkersChanged: Symbol('MarkersChanged')
1154 }; 1936 };
1155 1937
1938
1156 /** 1939 /**
1157 * @param {!WebInspector.RemoteObject} object 1940 * @implements {DOMAgent.Dispatcher}
1941 * @unrestricted
1158 */ 1942 */
1159 WebInspector.DOMModel.highlightObjectAsDOMNode = function(object) 1943 WebInspector.DOMDispatcher = class {
1160 { 1944 /**
1161 var domModel = WebInspector.DOMModel.fromTarget(object.target()); 1945 * @param {!WebInspector.DOMModel} domModel
1162 if (domModel) 1946 */
1163 domModel.highlightDOMNode(undefined, undefined, undefined, object.object Id); 1947 constructor(domModel) {
1164 };
1165
1166 /**
1167 * @return {!Array<!WebInspector.DOMModel>}
1168 */
1169 WebInspector.DOMModel.instances = function()
1170 {
1171 var result = [];
1172 for (var target of WebInspector.targetManager.targets()) {
1173 var domModel = WebInspector.DOMModel.fromTarget(target);
1174 if (domModel)
1175 result.push(domModel);
1176 }
1177 return result;
1178 };
1179
1180 WebInspector.DOMModel.hideDOMNodeHighlight = function()
1181 {
1182 for (var domModel of WebInspector.DOMModel.instances())
1183 domModel.highlightDOMNode(0);
1184 };
1185
1186 WebInspector.DOMModel.muteHighlight = function()
1187 {
1188 WebInspector.DOMModel.hideDOMNodeHighlight();
1189 WebInspector.DOMModel._highlightDisabled = true;
1190 };
1191
1192 WebInspector.DOMModel.unmuteHighlight = function()
1193 {
1194 WebInspector.DOMModel._highlightDisabled = false;
1195 };
1196
1197 WebInspector.DOMModel.cancelSearch = function()
1198 {
1199 for (var domModel of WebInspector.DOMModel.instances())
1200 domModel._cancelSearch();
1201 };
1202
1203 WebInspector.DOMModel.prototype = {
1204 /**
1205 * @param {!WebInspector.DOMNode} node
1206 */
1207 _scheduleMutationEvent: function(node)
1208 {
1209 if (!this.hasEventListeners(WebInspector.DOMModel.Events.DOMMutated))
1210 return;
1211
1212 this._lastMutationId = (this._lastMutationId || 0) + 1;
1213 Promise.resolve().then(callObserve.bind(this, node, this._lastMutationId ));
1214
1215 /**
1216 * @this {WebInspector.DOMModel}
1217 * @param {!WebInspector.DOMNode} node
1218 * @param {number} mutationId
1219 */
1220 function callObserve(node, mutationId)
1221 {
1222 if (!this.hasEventListeners(WebInspector.DOMModel.Events.DOMMutated) || this._lastMutationId !== mutationId)
1223 return;
1224
1225 this.dispatchEventToListeners(WebInspector.DOMModel.Events.DOMMutate d, node);
1226 }
1227 },
1228
1229 /**
1230 * @param {function(!WebInspector.DOMDocument)=} callback
1231 */
1232 requestDocument: function(callback)
1233 {
1234 if (this._document) {
1235 if (callback)
1236 callback(this._document);
1237 return;
1238 }
1239
1240 if (this._pendingDocumentRequestCallbacks) {
1241 this._pendingDocumentRequestCallbacks.push(callback);
1242 return;
1243 }
1244
1245 this._pendingDocumentRequestCallbacks = [callback];
1246
1247 /**
1248 * @this {WebInspector.DOMModel}
1249 * @param {?Protocol.Error} error
1250 * @param {!DOMAgent.Node} root
1251 */
1252 function onDocumentAvailable(error, root)
1253 {
1254 if (!error)
1255 this._setDocument(root);
1256
1257 for (var i = 0; i < this._pendingDocumentRequestCallbacks.length; ++ i) {
1258 var callback = this._pendingDocumentRequestCallbacks[i];
1259 if (callback)
1260 callback(this._document);
1261 }
1262 delete this._pendingDocumentRequestCallbacks;
1263 }
1264
1265 this._agent.getDocument(undefined, undefined, onDocumentAvailable.bind(t his));
1266 },
1267
1268 /**
1269 * @return {?WebInspector.DOMDocument}
1270 */
1271 existingDocument: function()
1272 {
1273 return this._document;
1274 },
1275
1276 /**
1277 * @param {!RuntimeAgent.RemoteObjectId} objectId
1278 * @param {function(?WebInspector.DOMNode)=} callback
1279 */
1280 pushNodeToFrontend: function(objectId, callback)
1281 {
1282 /**
1283 * @param {?DOMAgent.NodeId} nodeId
1284 * @this {!WebInspector.DOMModel}
1285 */
1286 function mycallback(nodeId)
1287 {
1288 callback(nodeId ? this.nodeForId(nodeId) : null);
1289 }
1290 this._dispatchWhenDocumentAvailable(this._agent.requestNode.bind(this._a gent, objectId), mycallback.bind(this));
1291 },
1292
1293 /**
1294 * @param {string} path
1295 * @param {function(?number)=} callback
1296 */
1297 pushNodeByPathToFrontend: function(path, callback)
1298 {
1299 this._dispatchWhenDocumentAvailable(this._agent.pushNodeByPathToFrontend .bind(this._agent, path), callback);
1300 },
1301
1302 /**
1303 * @param {!Set<number>} backendNodeIds
1304 * @param {function(?Map<number, ?WebInspector.DOMNode>)} callback
1305 */
1306 pushNodesByBackendIdsToFrontend: function(backendNodeIds, callback)
1307 {
1308 var backendNodeIdsArray = backendNodeIds.valuesArray();
1309 /**
1310 * @param {?Array<!DOMAgent.NodeId>} nodeIds
1311 * @this {!WebInspector.DOMModel}
1312 */
1313 function mycallback(nodeIds)
1314 {
1315 if (!nodeIds) {
1316 callback(null);
1317 return;
1318 }
1319 /** @type {!Map<number, ?WebInspector.DOMNode>} */
1320 var map = new Map();
1321 for (var i = 0; i < nodeIds.length; ++i) {
1322 if (nodeIds[i])
1323 map.set(backendNodeIdsArray[i], this.nodeForId(nodeIds[i]));
1324 }
1325 callback(map);
1326 }
1327 this._dispatchWhenDocumentAvailable(this._agent.pushNodesByBackendIdsToF rontend.bind(this._agent, backendNodeIdsArray), mycallback.bind(this));
1328 },
1329
1330 /**
1331 * @param {function(!T)=} callback
1332 * @return {function(?Protocol.Error, !T=)|undefined}
1333 * @template T
1334 */
1335 _wrapClientCallback: function(callback)
1336 {
1337 if (!callback)
1338 return;
1339 /**
1340 * @param {?Protocol.Error} error
1341 * @param {!T=} result
1342 * @template T
1343 */
1344 var wrapper = function(error, result)
1345 {
1346 // Caller is responsible for handling the actual error.
1347 callback(error ? null : result);
1348 };
1349 return wrapper;
1350 },
1351
1352 /**
1353 * @param {function(function(?Protocol.Error, !T=)=)} func
1354 * @param {function(!T)=} callback
1355 * @template T
1356 */
1357 _dispatchWhenDocumentAvailable: function(func, callback)
1358 {
1359 var callbackWrapper = this._wrapClientCallback(callback);
1360
1361 /**
1362 * @this {WebInspector.DOMModel}
1363 */
1364 function onDocumentAvailable()
1365 {
1366 if (this._document)
1367 func(callbackWrapper);
1368 else {
1369 if (callbackWrapper)
1370 callbackWrapper("No document");
1371 }
1372 }
1373 this.requestDocument(onDocumentAvailable.bind(this));
1374 },
1375
1376 /**
1377 * @param {!DOMAgent.NodeId} nodeId
1378 * @param {string} name
1379 * @param {string} value
1380 */
1381 _attributeModified: function(nodeId, name, value)
1382 {
1383 var node = this._idToDOMNode[nodeId];
1384 if (!node)
1385 return;
1386
1387 node._setAttribute(name, value);
1388 this.dispatchEventToListeners(WebInspector.DOMModel.Events.AttrModified, { node: node, name: name });
1389 this._scheduleMutationEvent(node);
1390 },
1391
1392 /**
1393 * @param {!DOMAgent.NodeId} nodeId
1394 * @param {string} name
1395 */
1396 _attributeRemoved: function(nodeId, name)
1397 {
1398 var node = this._idToDOMNode[nodeId];
1399 if (!node)
1400 return;
1401 node._removeAttribute(name);
1402 this.dispatchEventToListeners(WebInspector.DOMModel.Events.AttrRemoved, { node: node, name: name });
1403 this._scheduleMutationEvent(node);
1404 },
1405
1406 /**
1407 * @param {!Array.<!DOMAgent.NodeId>} nodeIds
1408 */
1409 _inlineStyleInvalidated: function(nodeIds)
1410 {
1411 for (var i = 0; i < nodeIds.length; ++i)
1412 this._attributeLoadNodeIds[nodeIds[i]] = true;
1413 if ("_loadNodeAttributesTimeout" in this)
1414 return;
1415 this._loadNodeAttributesTimeout = setTimeout(this._loadNodeAttributes.bi nd(this), 20);
1416 },
1417
1418 _loadNodeAttributes: function()
1419 {
1420 /**
1421 * @this {WebInspector.DOMModel}
1422 * @param {!DOMAgent.NodeId} nodeId
1423 * @param {?Protocol.Error} error
1424 * @param {!Array.<string>} attributes
1425 */
1426 function callback(nodeId, error, attributes)
1427 {
1428 if (error) {
1429 // We are calling _loadNodeAttributes asynchronously, it is ok i f node is not found.
1430 return;
1431 }
1432 var node = this._idToDOMNode[nodeId];
1433 if (node) {
1434 if (node._setAttributesPayload(attributes)) {
1435 this.dispatchEventToListeners(WebInspector.DOMModel.Events.A ttrModified, { node: node, name: "style" });
1436 this._scheduleMutationEvent(node);
1437 }
1438 }
1439 }
1440
1441 delete this._loadNodeAttributesTimeout;
1442
1443 for (var nodeId in this._attributeLoadNodeIds) {
1444 var nodeIdAsNumber = parseInt(nodeId, 10);
1445 this._agent.getAttributes(nodeIdAsNumber, callback.bind(this, nodeId AsNumber));
1446 }
1447 this._attributeLoadNodeIds = {};
1448 },
1449
1450 /**
1451 * @param {!DOMAgent.NodeId} nodeId
1452 * @param {string} newValue
1453 */
1454 _characterDataModified: function(nodeId, newValue)
1455 {
1456 var node = this._idToDOMNode[nodeId];
1457 node._nodeValue = newValue;
1458 this.dispatchEventToListeners(WebInspector.DOMModel.Events.CharacterData Modified, node);
1459 this._scheduleMutationEvent(node);
1460 },
1461
1462 /**
1463 * @param {!DOMAgent.NodeId} nodeId
1464 * @return {?WebInspector.DOMNode}
1465 */
1466 nodeForId: function(nodeId)
1467 {
1468 return this._idToDOMNode[nodeId] || null;
1469 },
1470
1471 _documentUpdated: function()
1472 {
1473 this._setDocument(null);
1474 },
1475
1476 /**
1477 * @param {?DOMAgent.Node} payload
1478 */
1479 _setDocument: function(payload)
1480 {
1481 this._idToDOMNode = {};
1482 if (payload && "nodeId" in payload)
1483 this._document = new WebInspector.DOMDocument(this, payload);
1484 else
1485 this._document = null;
1486 this.dispatchEventToListeners(WebInspector.DOMModel.Events.DocumentUpdat ed, this._document);
1487 },
1488
1489 /**
1490 * @param {!DOMAgent.Node} payload
1491 */
1492 _setDetachedRoot: function(payload)
1493 {
1494 if (payload.nodeName === "#document")
1495 new WebInspector.DOMDocument(this, payload);
1496 else
1497 WebInspector.DOMNode.create(this, null, false, payload);
1498 },
1499
1500 /**
1501 * @param {!DOMAgent.NodeId} parentId
1502 * @param {!Array.<!DOMAgent.Node>} payloads
1503 */
1504 _setChildNodes: function(parentId, payloads)
1505 {
1506 if (!parentId && payloads.length) {
1507 this._setDetachedRoot(payloads[0]);
1508 return;
1509 }
1510
1511 var parent = this._idToDOMNode[parentId];
1512 parent._setChildrenPayload(payloads);
1513 },
1514
1515 /**
1516 * @param {!DOMAgent.NodeId} nodeId
1517 * @param {number} newValue
1518 */
1519 _childNodeCountUpdated: function(nodeId, newValue)
1520 {
1521 var node = this._idToDOMNode[nodeId];
1522 node._childNodeCount = newValue;
1523 this.dispatchEventToListeners(WebInspector.DOMModel.Events.ChildNodeCoun tUpdated, node);
1524 this._scheduleMutationEvent(node);
1525 },
1526
1527 /**
1528 * @param {!DOMAgent.NodeId} parentId
1529 * @param {!DOMAgent.NodeId} prevId
1530 * @param {!DOMAgent.Node} payload
1531 */
1532 _childNodeInserted: function(parentId, prevId, payload)
1533 {
1534 var parent = this._idToDOMNode[parentId];
1535 var prev = this._idToDOMNode[prevId];
1536 var node = parent._insertChild(prev, payload);
1537 this._idToDOMNode[node.id] = node;
1538 this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeInserted, node);
1539 this._scheduleMutationEvent(node);
1540 },
1541
1542 /**
1543 * @param {!DOMAgent.NodeId} parentId
1544 * @param {!DOMAgent.NodeId} nodeId
1545 */
1546 _childNodeRemoved: function(parentId, nodeId)
1547 {
1548 var parent = this._idToDOMNode[parentId];
1549 var node = this._idToDOMNode[nodeId];
1550 parent._removeChild(node);
1551 this._unbind(node);
1552 this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeRemoved, {node: node, parent: parent});
1553 this._scheduleMutationEvent(node);
1554 },
1555
1556 /**
1557 * @param {!DOMAgent.NodeId} hostId
1558 * @param {!DOMAgent.Node} root
1559 */
1560 _shadowRootPushed: function(hostId, root)
1561 {
1562 var host = this._idToDOMNode[hostId];
1563 if (!host)
1564 return;
1565 var node = WebInspector.DOMNode.create(this, host.ownerDocument, true, r oot);
1566 node.parentNode = host;
1567 this._idToDOMNode[node.id] = node;
1568 host._shadowRoots.unshift(node);
1569 this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeInserted, node);
1570 this._scheduleMutationEvent(node);
1571 },
1572
1573 /**
1574 * @param {!DOMAgent.NodeId} hostId
1575 * @param {!DOMAgent.NodeId} rootId
1576 */
1577 _shadowRootPopped: function(hostId, rootId)
1578 {
1579 var host = this._idToDOMNode[hostId];
1580 if (!host)
1581 return;
1582 var root = this._idToDOMNode[rootId];
1583 if (!root)
1584 return;
1585 host._removeChild(root);
1586 this._unbind(root);
1587 this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeRemoved, {node: root, parent: host});
1588 this._scheduleMutationEvent(root);
1589 },
1590
1591 /**
1592 * @param {!DOMAgent.NodeId} parentId
1593 * @param {!DOMAgent.Node} pseudoElement
1594 */
1595 _pseudoElementAdded: function(parentId, pseudoElement)
1596 {
1597 var parent = this._idToDOMNode[parentId];
1598 if (!parent)
1599 return;
1600 var node = WebInspector.DOMNode.create(this, parent.ownerDocument, false , pseudoElement);
1601 node.parentNode = parent;
1602 this._idToDOMNode[node.id] = node;
1603 console.assert(!parent._pseudoElements.get(node.pseudoType()));
1604 parent._pseudoElements.set(node.pseudoType(), node);
1605 this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeInserted, node);
1606 this._scheduleMutationEvent(node);
1607 },
1608
1609 /**
1610 * @param {!DOMAgent.NodeId} parentId
1611 * @param {!DOMAgent.NodeId} pseudoElementId
1612 */
1613 _pseudoElementRemoved: function(parentId, pseudoElementId)
1614 {
1615 var parent = this._idToDOMNode[parentId];
1616 if (!parent)
1617 return;
1618 var pseudoElement = this._idToDOMNode[pseudoElementId];
1619 if (!pseudoElement)
1620 return;
1621 parent._removeChild(pseudoElement);
1622 this._unbind(pseudoElement);
1623 this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeRemoved, {node: pseudoElement, parent: parent});
1624 this._scheduleMutationEvent(pseudoElement);
1625 },
1626
1627 /**
1628 * @param {!DOMAgent.NodeId} insertionPointId
1629 * @param {!Array.<!DOMAgent.BackendNode>} distributedNodes
1630 */
1631 _distributedNodesUpdated: function(insertionPointId, distributedNodes)
1632 {
1633 var insertionPoint = this._idToDOMNode[insertionPointId];
1634 if (!insertionPoint)
1635 return;
1636 insertionPoint._setDistributedNodePayloads(distributedNodes);
1637 this.dispatchEventToListeners(WebInspector.DOMModel.Events.DistributedNo desChanged, insertionPoint);
1638 this._scheduleMutationEvent(insertionPoint);
1639 },
1640
1641 /**
1642 * @param {!WebInspector.DOMNode} node
1643 */
1644 _unbind: function(node)
1645 {
1646 delete this._idToDOMNode[node.id];
1647 for (var i = 0; node._children && i < node._children.length; ++i)
1648 this._unbind(node._children[i]);
1649 for (var i = 0; i < node._shadowRoots.length; ++i)
1650 this._unbind(node._shadowRoots[i]);
1651 var pseudoElements = node.pseudoElements();
1652 for (var value of pseudoElements.values())
1653 this._unbind(value);
1654 if (node._templateContent)
1655 this._unbind(node._templateContent);
1656 },
1657
1658 /**
1659 * @param {!DOMAgent.BackendNodeId} backendNodeId
1660 */
1661 _inspectNodeRequested: function(backendNodeId)
1662 {
1663 var deferredNode = new WebInspector.DeferredDOMNode(this.target(), backe ndNodeId);
1664 this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeInspected , deferredNode);
1665 },
1666
1667 /**
1668 * @param {string} query
1669 * @param {boolean} includeUserAgentShadowDOM
1670 * @param {function(number)} searchCallback
1671 */
1672 performSearch: function(query, includeUserAgentShadowDOM, searchCallback)
1673 {
1674 WebInspector.DOMModel.cancelSearch();
1675
1676 /**
1677 * @param {?Protocol.Error} error
1678 * @param {string} searchId
1679 * @param {number} resultsCount
1680 * @this {WebInspector.DOMModel}
1681 */
1682 function callback(error, searchId, resultsCount)
1683 {
1684 this._searchId = searchId;
1685 searchCallback(resultsCount);
1686 }
1687 this._agent.performSearch(query, includeUserAgentShadowDOM, callback.bin d(this));
1688 },
1689
1690 /**
1691 * @param {string} query
1692 * @param {boolean} includeUserAgentShadowDOM
1693 * @return {!Promise.<number>}
1694 */
1695 performSearchPromise: function(query, includeUserAgentShadowDOM)
1696 {
1697 return new Promise(performSearch.bind(this));
1698
1699 /**
1700 * @param {function(number)} resolve
1701 * @this {WebInspector.DOMModel}
1702 */
1703 function performSearch(resolve)
1704 {
1705 this._agent.performSearch(query, includeUserAgentShadowDOM, callback .bind(this));
1706
1707 /**
1708 * @param {?Protocol.Error} error
1709 * @param {string} searchId
1710 * @param {number} resultsCount
1711 * @this {WebInspector.DOMModel}
1712 */
1713 function callback(error, searchId, resultsCount)
1714 {
1715 if (!error)
1716 this._searchId = searchId;
1717 resolve(error ? 0 : resultsCount);
1718 }
1719 }
1720 },
1721
1722 /**
1723 * @param {number} index
1724 * @param {?function(?WebInspector.DOMNode)} callback
1725 */
1726 searchResult: function(index, callback)
1727 {
1728 if (this._searchId)
1729 this._agent.getSearchResults(this._searchId, index, index + 1, searc hResultsCallback.bind(this));
1730 else
1731 callback(null);
1732
1733 /**
1734 * @param {?Protocol.Error} error
1735 * @param {!Array.<number>} nodeIds
1736 * @this {WebInspector.DOMModel}
1737 */
1738 function searchResultsCallback(error, nodeIds)
1739 {
1740 if (error) {
1741 console.error(error);
1742 callback(null);
1743 return;
1744 }
1745 if (nodeIds.length !== 1)
1746 return;
1747
1748 callback(this.nodeForId(nodeIds[0]));
1749 }
1750 },
1751
1752 _cancelSearch: function()
1753 {
1754 if (this._searchId) {
1755 this._agent.discardSearchResults(this._searchId);
1756 delete this._searchId;
1757 }
1758 },
1759
1760 /**
1761 * @param {!DOMAgent.NodeId} nodeId
1762 * @return {!Promise<!Array<string>>}
1763 */
1764 classNamesPromise: function(nodeId)
1765 {
1766 return new Promise(promiseBody.bind(this));
1767
1768 /**
1769 * @param {function(!Array<string>)} fulfill
1770 * @this {WebInspector.DOMModel}
1771 */
1772 function promiseBody(fulfill)
1773 {
1774 this._agent.collectClassNamesFromSubtree(nodeId, classNamesCallback) ;
1775
1776 /**
1777 * @param {?string} error
1778 * @param {?Array<string>} classNames
1779 */
1780 function classNamesCallback(error, classNames)
1781 {
1782 if (!error && classNames)
1783 fulfill(classNames);
1784 else
1785 fulfill([]);
1786 }
1787 }
1788 },
1789
1790 /**
1791 * @param {!DOMAgent.NodeId} nodeId
1792 * @param {string} selectors
1793 * @param {function(?DOMAgent.NodeId)=} callback
1794 */
1795 querySelector: function(nodeId, selectors, callback)
1796 {
1797 this._agent.querySelector(nodeId, selectors, this._wrapClientCallback(ca llback));
1798 },
1799
1800 /**
1801 * @param {!DOMAgent.NodeId} nodeId
1802 * @param {string} selectors
1803 * @param {function(!Array.<!DOMAgent.NodeId>=)=} callback
1804 */
1805 querySelectorAll: function(nodeId, selectors, callback)
1806 {
1807 this._agent.querySelectorAll(nodeId, selectors, this._wrapClientCallback (callback));
1808 },
1809
1810 /**
1811 * @param {!DOMAgent.NodeId=} nodeId
1812 * @param {string=} mode
1813 * @param {!DOMAgent.BackendNodeId=} backendNodeId
1814 * @param {!RuntimeAgent.RemoteObjectId=} objectId
1815 */
1816 highlightDOMNode: function(nodeId, mode, backendNodeId, objectId)
1817 {
1818 this.highlightDOMNodeWithConfig(nodeId, { mode: mode }, backendNodeId, o bjectId);
1819 },
1820
1821 /**
1822 * @param {!DOMAgent.NodeId=} nodeId
1823 * @param {!{mode: (string|undefined), showInfo: (boolean|undefined), select ors: (string|undefined)}=} config
1824 * @param {!DOMAgent.BackendNodeId=} backendNodeId
1825 * @param {!RuntimeAgent.RemoteObjectId=} objectId
1826 */
1827 highlightDOMNodeWithConfig: function(nodeId, config, backendNodeId, objectId )
1828 {
1829 if (WebInspector.DOMModel._highlightDisabled)
1830 return;
1831 config = config || { mode: "all", showInfo: undefined, selectors: undefi ned };
1832 if (this._hideDOMNodeHighlightTimeout) {
1833 clearTimeout(this._hideDOMNodeHighlightTimeout);
1834 delete this._hideDOMNodeHighlightTimeout;
1835 }
1836 var highlightConfig = this._buildHighlightConfig(config.mode);
1837 if (typeof config.showInfo !== "undefined")
1838 highlightConfig.showInfo = config.showInfo;
1839 if (typeof config.selectors !== "undefined")
1840 highlightConfig.selectorList = config.selectors;
1841 this._highlighter.highlightDOMNode(this.nodeForId(nodeId || 0), highligh tConfig, backendNodeId, objectId);
1842 },
1843
1844 /**
1845 * @param {!DOMAgent.NodeId} nodeId
1846 */
1847 highlightDOMNodeForTwoSeconds: function(nodeId)
1848 {
1849 this.highlightDOMNode(nodeId);
1850 this._hideDOMNodeHighlightTimeout = setTimeout(WebInspector.DOMModel.hid eDOMNodeHighlight.bind(WebInspector.DOMModel), 2000);
1851 },
1852
1853 /**
1854 * @param {!PageAgent.FrameId} frameId
1855 */
1856 highlightFrame: function(frameId)
1857 {
1858 if (WebInspector.DOMModel._highlightDisabled)
1859 return;
1860 this._highlighter.highlightFrame(frameId);
1861 },
1862
1863 /**
1864 * @param {!DOMAgent.InspectMode} mode
1865 * @param {function(?Protocol.Error)=} callback
1866 */
1867 setInspectMode: function(mode, callback)
1868 {
1869 /**
1870 * @this {WebInspector.DOMModel}
1871 */
1872 function onDocumentAvailable()
1873 {
1874 this._inspectModeEnabled = mode !== DOMAgent.InspectMode.None;
1875 this.dispatchEventToListeners(WebInspector.DOMModel.Events.InspectMo deWillBeToggled, this._inspectModeEnabled);
1876 this._highlighter.setInspectMode(mode, this._buildHighlightConfig(), callback);
1877 }
1878 this.requestDocument(onDocumentAvailable.bind(this));
1879 },
1880
1881 /**
1882 * @return {boolean}
1883 */
1884 inspectModeEnabled: function()
1885 {
1886 return this._inspectModeEnabled;
1887 },
1888
1889 /**
1890 * @param {string=} mode
1891 * @return {!DOMAgent.HighlightConfig}
1892 */
1893 _buildHighlightConfig: function(mode)
1894 {
1895 mode = mode || "all";
1896 var showRulers = WebInspector.moduleSetting("showMetricsRulers").get();
1897 var highlightConfig = { showInfo: mode === "all", showRulers: showRulers , showExtensionLines: showRulers };
1898 if (mode === "all" || mode === "content")
1899 highlightConfig.contentColor = WebInspector.Color.PageHighlight.Cont ent.toProtocolRGBA();
1900
1901 if (mode === "all" || mode === "padding")
1902 highlightConfig.paddingColor = WebInspector.Color.PageHighlight.Padd ing.toProtocolRGBA();
1903
1904 if (mode === "all" || mode === "border")
1905 highlightConfig.borderColor = WebInspector.Color.PageHighlight.Borde r.toProtocolRGBA();
1906
1907 if (mode === "all" || mode === "margin")
1908 highlightConfig.marginColor = WebInspector.Color.PageHighlight.Margi n.toProtocolRGBA();
1909
1910 if (mode === "all") {
1911 highlightConfig.eventTargetColor = WebInspector.Color.PageHighlight. EventTarget.toProtocolRGBA();
1912 highlightConfig.shapeColor = WebInspector.Color.PageHighlight.Shape. toProtocolRGBA();
1913 highlightConfig.shapeMarginColor = WebInspector.Color.PageHighlight. ShapeMargin.toProtocolRGBA();
1914 highlightConfig.displayAsMaterial = Runtime.experiments.isEnabled("i nspectTooltip");
1915 }
1916 return highlightConfig;
1917 },
1918
1919 /**
1920 * @param {!WebInspector.DOMNode} node
1921 * @param {function(?Protocol.Error, ...)=} callback
1922 * @return {function(...)}
1923 * @template T
1924 */
1925 _markRevision: function(node, callback)
1926 {
1927 /**
1928 * @param {?Protocol.Error} error
1929 * @this {WebInspector.DOMModel}
1930 */
1931 function wrapperFunction(error)
1932 {
1933 if (!error)
1934 this.markUndoableState();
1935
1936 if (callback)
1937 callback.apply(this, arguments);
1938 }
1939 return wrapperFunction.bind(this);
1940 },
1941
1942 markUndoableState: function()
1943 {
1944 this._agent.markUndoableState();
1945 },
1946
1947 /**
1948 * @param {function(?Protocol.Error)=} callback
1949 */
1950 undo: function(callback)
1951 {
1952 /**
1953 * @param {?Protocol.Error} error
1954 * @this {WebInspector.DOMModel}
1955 */
1956 function mycallback(error)
1957 {
1958 this.dispatchEventToListeners(WebInspector.DOMModel.Events.UndoRedoC ompleted);
1959 callback(error);
1960 }
1961
1962 this.dispatchEventToListeners(WebInspector.DOMModel.Events.UndoRedoReque sted);
1963 this._agent.undo(callback);
1964 },
1965
1966 /**
1967 * @param {function(?Protocol.Error)=} callback
1968 */
1969 redo: function(callback)
1970 {
1971 /**
1972 * @param {?Protocol.Error} error
1973 * @this {WebInspector.DOMModel}
1974 */
1975 function mycallback(error)
1976 {
1977 this.dispatchEventToListeners(WebInspector.DOMModel.Events.UndoRedoC ompleted);
1978 callback(error);
1979 }
1980
1981 this.dispatchEventToListeners(WebInspector.DOMModel.Events.UndoRedoReque sted);
1982 this._agent.redo(callback);
1983 },
1984
1985 /**
1986 * @param {?WebInspector.DOMNodeHighlighter} highlighter
1987 */
1988 setHighlighter: function(highlighter)
1989 {
1990 this._highlighter = highlighter || this._defaultHighlighter;
1991 },
1992
1993 /**
1994 * @param {number} x
1995 * @param {number} y
1996 * @param {function(?WebInspector.DOMNode)} callback
1997 */
1998 nodeForLocation: function(x, y, callback)
1999 {
2000 this._agent.getNodeForLocation(x, y, mycallback.bind(this));
2001
2002 /**
2003 * @param {?Protocol.Error} error
2004 * @param {number} nodeId
2005 * @this {WebInspector.DOMModel}
2006 */
2007 function mycallback(error, nodeId)
2008 {
2009 if (error) {
2010 callback(null);
2011 return;
2012 }
2013 callback(this.nodeForId(nodeId));
2014 }
2015 },
2016
2017 /**
2018 * @param {!WebInspector.RemoteObject} object
2019 * @param {function(?WebInspector.DOMNode)} callback
2020 */
2021 pushObjectAsNodeToFrontend: function(object, callback)
2022 {
2023 if (object.isNode())
2024 this.pushNodeToFrontend(object.objectId, callback);
2025 else
2026 callback(null);
2027 },
2028
2029 /**
2030 * @override
2031 * @return {!Promise}
2032 */
2033 suspendModel: function()
2034 {
2035 return new Promise(promiseBody.bind(this));
2036
2037 /**
2038 * @param {function()} fulfill
2039 * @this {WebInspector.DOMModel}
2040 */
2041 function promiseBody(fulfill)
2042 {
2043 this._agent.disable(callback.bind(this));
2044
2045 /**
2046 * @this {WebInspector.DOMModel}
2047 */
2048 function callback()
2049 {
2050 this._setDocument(null);
2051 fulfill();
2052 }
2053 }
2054 },
2055
2056 /**
2057 * @override
2058 * @return {!Promise}
2059 */
2060 resumeModel: function()
2061 {
2062 return new Promise(promiseBody.bind(this));
2063
2064 /**
2065 * @param {function()} fulfill
2066 * @this {WebInspector.DOMModel}
2067 */
2068 function promiseBody(fulfill)
2069 {
2070 this._agent.enable(fulfill);
2071 }
2072 },
2073
2074 /**
2075 * @param {!DOMAgent.NodeId} nodeId
2076 */
2077 nodeHighlightRequested: function(nodeId)
2078 {
2079 var node = this.nodeForId(nodeId);
2080 if (!node)
2081 return;
2082
2083 this.dispatchEventToListeners(WebInspector.DOMModel.Events.NodeHighlight edInOverlay, node);
2084 },
2085
2086 __proto__: WebInspector.SDKModel.prototype
2087 };
2088
2089 /**
2090 * @constructor
2091 * @implements {DOMAgent.Dispatcher}
2092 * @param {!WebInspector.DOMModel} domModel
2093 */
2094 WebInspector.DOMDispatcher = function(domModel)
2095 {
2096 this._domModel = domModel; 1948 this._domModel = domModel;
2097 }; 1949 }
2098 1950
2099 WebInspector.DOMDispatcher.prototype = { 1951 /**
2100 /** 1952 * @override
2101 * @override 1953 */
2102 */ 1954 documentUpdated() {
2103 documentUpdated: function() 1955 this._domModel._documentUpdated();
2104 { 1956 }
2105 this._domModel._documentUpdated(); 1957
2106 }, 1958 /**
2107 1959 * @override
2108 /** 1960 * @param {!DOMAgent.NodeId} nodeId
2109 * @override 1961 */
2110 * @param {!DOMAgent.NodeId} nodeId 1962 inspectNodeRequested(nodeId) {
2111 */ 1963 this._domModel._inspectNodeRequested(nodeId);
2112 inspectNodeRequested: function(nodeId) 1964 }
2113 { 1965
2114 this._domModel._inspectNodeRequested(nodeId); 1966 /**
2115 }, 1967 * @override
2116 1968 * @param {!DOMAgent.NodeId} nodeId
2117 /** 1969 * @param {string} name
2118 * @override 1970 * @param {string} value
2119 * @param {!DOMAgent.NodeId} nodeId 1971 */
2120 * @param {string} name 1972 attributeModified(nodeId, name, value) {
2121 * @param {string} value 1973 this._domModel._attributeModified(nodeId, name, value);
2122 */ 1974 }
2123 attributeModified: function(nodeId, name, value) 1975
2124 { 1976 /**
2125 this._domModel._attributeModified(nodeId, name, value); 1977 * @override
2126 }, 1978 * @param {!DOMAgent.NodeId} nodeId
2127 1979 * @param {string} name
2128 /** 1980 */
2129 * @override 1981 attributeRemoved(nodeId, name) {
2130 * @param {!DOMAgent.NodeId} nodeId 1982 this._domModel._attributeRemoved(nodeId, name);
2131 * @param {string} name 1983 }
2132 */ 1984
2133 attributeRemoved: function(nodeId, name) 1985 /**
2134 { 1986 * @override
2135 this._domModel._attributeRemoved(nodeId, name); 1987 * @param {!Array.<!DOMAgent.NodeId>} nodeIds
2136 }, 1988 */
2137 1989 inlineStyleInvalidated(nodeIds) {
2138 /** 1990 this._domModel._inlineStyleInvalidated(nodeIds);
2139 * @override 1991 }
2140 * @param {!Array.<!DOMAgent.NodeId>} nodeIds 1992
2141 */ 1993 /**
2142 inlineStyleInvalidated: function(nodeIds) 1994 * @override
2143 { 1995 * @param {!DOMAgent.NodeId} nodeId
2144 this._domModel._inlineStyleInvalidated(nodeIds); 1996 * @param {string} characterData
2145 }, 1997 */
2146 1998 characterDataModified(nodeId, characterData) {
2147 /** 1999 this._domModel._characterDataModified(nodeId, characterData);
2148 * @override 2000 }
2149 * @param {!DOMAgent.NodeId} nodeId 2001
2150 * @param {string} characterData 2002 /**
2151 */ 2003 * @override
2152 characterDataModified: function(nodeId, characterData) 2004 * @param {!DOMAgent.NodeId} parentId
2153 { 2005 * @param {!Array.<!DOMAgent.Node>} payloads
2154 this._domModel._characterDataModified(nodeId, characterData); 2006 */
2155 }, 2007 setChildNodes(parentId, payloads) {
2156 2008 this._domModel._setChildNodes(parentId, payloads);
2157 /** 2009 }
2158 * @override 2010
2159 * @param {!DOMAgent.NodeId} parentId 2011 /**
2160 * @param {!Array.<!DOMAgent.Node>} payloads 2012 * @override
2161 */ 2013 * @param {!DOMAgent.NodeId} nodeId
2162 setChildNodes: function(parentId, payloads) 2014 * @param {number} childNodeCount
2163 { 2015 */
2164 this._domModel._setChildNodes(parentId, payloads); 2016 childNodeCountUpdated(nodeId, childNodeCount) {
2165 }, 2017 this._domModel._childNodeCountUpdated(nodeId, childNodeCount);
2166 2018 }
2167 /** 2019
2168 * @override 2020 /**
2169 * @param {!DOMAgent.NodeId} nodeId 2021 * @override
2170 * @param {number} childNodeCount 2022 * @param {!DOMAgent.NodeId} parentNodeId
2171 */ 2023 * @param {!DOMAgent.NodeId} previousNodeId
2172 childNodeCountUpdated: function(nodeId, childNodeCount) 2024 * @param {!DOMAgent.Node} payload
2173 { 2025 */
2174 this._domModel._childNodeCountUpdated(nodeId, childNodeCount); 2026 childNodeInserted(parentNodeId, previousNodeId, payload) {
2175 }, 2027 this._domModel._childNodeInserted(parentNodeId, previousNodeId, payload);
2176 2028 }
2177 /** 2029
2178 * @override 2030 /**
2179 * @param {!DOMAgent.NodeId} parentNodeId 2031 * @override
2180 * @param {!DOMAgent.NodeId} previousNodeId 2032 * @param {!DOMAgent.NodeId} parentNodeId
2181 * @param {!DOMAgent.Node} payload 2033 * @param {!DOMAgent.NodeId} nodeId
2182 */ 2034 */
2183 childNodeInserted: function(parentNodeId, previousNodeId, payload) 2035 childNodeRemoved(parentNodeId, nodeId) {
2184 { 2036 this._domModel._childNodeRemoved(parentNodeId, nodeId);
2185 this._domModel._childNodeInserted(parentNodeId, previousNodeId, payload) ; 2037 }
2186 }, 2038
2187 2039 /**
2188 /** 2040 * @override
2189 * @override 2041 * @param {!DOMAgent.NodeId} hostId
2190 * @param {!DOMAgent.NodeId} parentNodeId 2042 * @param {!DOMAgent.Node} root
2191 * @param {!DOMAgent.NodeId} nodeId 2043 */
2192 */ 2044 shadowRootPushed(hostId, root) {
2193 childNodeRemoved: function(parentNodeId, nodeId) 2045 this._domModel._shadowRootPushed(hostId, root);
2194 { 2046 }
2195 this._domModel._childNodeRemoved(parentNodeId, nodeId); 2047
2196 }, 2048 /**
2197 2049 * @override
2198 /** 2050 * @param {!DOMAgent.NodeId} hostId
2199 * @override 2051 * @param {!DOMAgent.NodeId} rootId
2200 * @param {!DOMAgent.NodeId} hostId 2052 */
2201 * @param {!DOMAgent.Node} root 2053 shadowRootPopped(hostId, rootId) {
2202 */ 2054 this._domModel._shadowRootPopped(hostId, rootId);
2203 shadowRootPushed: function(hostId, root) 2055 }
2204 { 2056
2205 this._domModel._shadowRootPushed(hostId, root); 2057 /**
2206 }, 2058 * @override
2207 2059 * @param {!DOMAgent.NodeId} parentId
2208 /** 2060 * @param {!DOMAgent.Node} pseudoElement
2209 * @override 2061 */
2210 * @param {!DOMAgent.NodeId} hostId 2062 pseudoElementAdded(parentId, pseudoElement) {
2211 * @param {!DOMAgent.NodeId} rootId 2063 this._domModel._pseudoElementAdded(parentId, pseudoElement);
2212 */ 2064 }
2213 shadowRootPopped: function(hostId, rootId) 2065
2214 { 2066 /**
2215 this._domModel._shadowRootPopped(hostId, rootId); 2067 * @override
2216 }, 2068 * @param {!DOMAgent.NodeId} parentId
2217 2069 * @param {!DOMAgent.NodeId} pseudoElementId
2218 /** 2070 */
2219 * @override 2071 pseudoElementRemoved(parentId, pseudoElementId) {
2220 * @param {!DOMAgent.NodeId} parentId 2072 this._domModel._pseudoElementRemoved(parentId, pseudoElementId);
2221 * @param {!DOMAgent.Node} pseudoElement 2073 }
2222 */ 2074
2223 pseudoElementAdded: function(parentId, pseudoElement) 2075 /**
2224 { 2076 * @override
2225 this._domModel._pseudoElementAdded(parentId, pseudoElement); 2077 * @param {!DOMAgent.NodeId} insertionPointId
2226 }, 2078 * @param {!Array.<!DOMAgent.BackendNode>} distributedNodes
2227 2079 */
2228 /** 2080 distributedNodesUpdated(insertionPointId, distributedNodes) {
2229 * @override 2081 this._domModel._distributedNodesUpdated(insertionPointId, distributedNodes);
2230 * @param {!DOMAgent.NodeId} parentId 2082 }
2231 * @param {!DOMAgent.NodeId} pseudoElementId 2083
2232 */ 2084 /**
2233 pseudoElementRemoved: function(parentId, pseudoElementId) 2085 * @override
2234 { 2086 * @param {!DOMAgent.NodeId} nodeId
2235 this._domModel._pseudoElementRemoved(parentId, pseudoElementId); 2087 */
2236 }, 2088 nodeHighlightRequested(nodeId) {
2237 2089 this._domModel.nodeHighlightRequested(nodeId);
2238 /** 2090 }
2239 * @override
2240 * @param {!DOMAgent.NodeId} insertionPointId
2241 * @param {!Array.<!DOMAgent.BackendNode>} distributedNodes
2242 */
2243 distributedNodesUpdated: function(insertionPointId, distributedNodes)
2244 {
2245 this._domModel._distributedNodesUpdated(insertionPointId, distributedNod es);
2246 },
2247
2248 /**
2249 * @override
2250 * @param {!DOMAgent.NodeId} nodeId
2251 */
2252 nodeHighlightRequested: function(nodeId)
2253 {
2254 this._domModel.nodeHighlightRequested(nodeId);
2255 }
2256 }; 2091 };
2257 2092
2258 /** 2093 /**
2259 * @interface 2094 * @interface
2260 */ 2095 */
2261 WebInspector.DOMNodeHighlighter = function() { 2096 WebInspector.DOMNodeHighlighter = function() {};
2097
2098 WebInspector.DOMNodeHighlighter.prototype = {
2099 /**
2100 * @param {?WebInspector.DOMNode} node
2101 * @param {!DOMAgent.HighlightConfig} config
2102 * @param {!DOMAgent.BackendNodeId=} backendNodeId
2103 * @param {!RuntimeAgent.RemoteObjectId=} objectId
2104 */
2105 highlightDOMNode: function(node, config, backendNodeId, objectId) {},
2106
2107 /**
2108 * @param {!DOMAgent.InspectMode} mode
2109 * @param {!DOMAgent.HighlightConfig} config
2110 * @param {function(?Protocol.Error)=} callback
2111 */
2112 setInspectMode: function(mode, config, callback) {},
2113
2114 /**
2115 * @param {!PageAgent.FrameId} frameId
2116 */
2117 highlightFrame: function(frameId) {}
2262 }; 2118 };
2263 2119
2264 WebInspector.DOMNodeHighlighter.prototype = { 2120 /**
2265 /** 2121 * @implements {WebInspector.DOMNodeHighlighter}
2266 * @param {?WebInspector.DOMNode} node 2122 * @unrestricted
2267 * @param {!DOMAgent.HighlightConfig} config 2123 */
2268 * @param {!DOMAgent.BackendNodeId=} backendNodeId 2124 WebInspector.DefaultDOMNodeHighlighter = class {
2269 * @param {!RuntimeAgent.RemoteObjectId=} objectId 2125 /**
2270 */ 2126 * @param {!Protocol.DOMAgent} agent
2271 highlightDOMNode: function(node, config, backendNodeId, objectId) {}, 2127 */
2272 2128 constructor(agent) {
2273 /** 2129 this._agent = agent;
2274 * @param {!DOMAgent.InspectMode} mode 2130 }
2275 * @param {!DOMAgent.HighlightConfig} config 2131
2276 * @param {function(?Protocol.Error)=} callback 2132 /**
2277 */ 2133 * @override
2278 setInspectMode: function(mode, config, callback) {}, 2134 * @param {?WebInspector.DOMNode} node
2279 2135 * @param {!DOMAgent.HighlightConfig} config
2280 /** 2136 * @param {!DOMAgent.BackendNodeId=} backendNodeId
2281 * @param {!PageAgent.FrameId} frameId 2137 * @param {!RuntimeAgent.RemoteObjectId=} objectId
2282 */ 2138 */
2283 highlightFrame: function(frameId) {} 2139 highlightDOMNode(node, config, backendNodeId, objectId) {
2140 if (objectId || node || backendNodeId)
2141 this._agent.highlightNode(config, (objectId || backendNodeId) ? undefined : node.id, backendNodeId, objectId);
2142 else
2143 this._agent.hideHighlight();
2144 }
2145
2146 /**
2147 * @override
2148 * @param {!DOMAgent.InspectMode} mode
2149 * @param {!DOMAgent.HighlightConfig} config
2150 * @param {function(?Protocol.Error)=} callback
2151 */
2152 setInspectMode(mode, config, callback) {
2153 this._agent.setInspectMode(mode, config, callback);
2154 }
2155
2156 /**
2157 * @override
2158 * @param {!PageAgent.FrameId} frameId
2159 */
2160 highlightFrame(frameId) {
2161 this._agent.highlightFrame(
2162 frameId, WebInspector.Color.PageHighlight.Content.toProtocolRGBA(),
2163 WebInspector.Color.PageHighlight.ContentOutline.toProtocolRGBA());
2164 }
2284 }; 2165 };
2285 2166
2286 /** 2167
2287 * @constructor
2288 * @implements {WebInspector.DOMNodeHighlighter}
2289 * @param {!Protocol.DOMAgent} agent
2290 */
2291 WebInspector.DefaultDOMNodeHighlighter = function(agent)
2292 {
2293 this._agent = agent;
2294 };
2295
2296 WebInspector.DefaultDOMNodeHighlighter.prototype = {
2297 /**
2298 * @override
2299 * @param {?WebInspector.DOMNode} node
2300 * @param {!DOMAgent.HighlightConfig} config
2301 * @param {!DOMAgent.BackendNodeId=} backendNodeId
2302 * @param {!RuntimeAgent.RemoteObjectId=} objectId
2303 */
2304 highlightDOMNode: function(node, config, backendNodeId, objectId)
2305 {
2306 if (objectId || node || backendNodeId)
2307 this._agent.highlightNode(config, (objectId || backendNodeId) ? unde fined : node.id, backendNodeId, objectId);
2308 else
2309 this._agent.hideHighlight();
2310 },
2311
2312 /**
2313 * @override
2314 * @param {!DOMAgent.InspectMode} mode
2315 * @param {!DOMAgent.HighlightConfig} config
2316 * @param {function(?Protocol.Error)=} callback
2317 */
2318 setInspectMode: function(mode, config, callback)
2319 {
2320 this._agent.setInspectMode(mode, config, callback);
2321 },
2322
2323 /**
2324 * @override
2325 * @param {!PageAgent.FrameId} frameId
2326 */
2327 highlightFrame: function(frameId)
2328 {
2329 this._agent.highlightFrame(frameId, WebInspector.Color.PageHighlight.Con tent.toProtocolRGBA(), WebInspector.Color.PageHighlight.ContentOutline.toProtoco lRGBA());
2330 }
2331 };
2332
2333 /**
2334 * @param {!WebInspector.Target} target
2335 * @return {?WebInspector.DOMModel}
2336 */
2337 WebInspector.DOMModel.fromTarget = function(target)
2338 {
2339 return /** @type {?WebInspector.DOMModel} */ (target.model(WebInspector.DOMM odel));
2340 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698