1 /* 2 * Copyright (C) 2009, 2010 Google Inc. All rights reserved. 3 * Copyright (C) 2009 Joseph Pecoraro 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /** 33 * @constructor 34 * @param {!WebInspector.DOMAgent} domAgent 35 * @param {?WebInspector.DOMDocument} doc 36 * @param {boolean} isInShadowTree 37 * @param {!DOMAgent.Node} payload 38 */ 39 WebInspector.DOMNode = function(domAgent, doc, isInShadowTree, payload) { 40 this._domAgent = domAgent; 41 this.ownerDocument = doc; 42 this._isInShadowTree = isInShadowTree; 43 44 this.id = payload.nodeId; 45 domAgent._idToDOMNode[this.id] = this; 46 this._nodeType = payload.nodeType; 47 this._nodeName = payload.nodeName; 48 this._localName = payload.localName; 49 this._nodeValue = payload.nodeValue; 50 this._pseudoType = payload.pseudoType; 51 this._shadowRootType = payload.shadowRootType; 52 53 this._shadowRoots = []; 54 55 this._attributes = []; 56 this._attributesMap = {}; 57 if (payload.attributes) 58 this._setAttributesPayload(payload.attributes); 59 60 this._userProperties = {}; 61 this._descendantUserPropertyCounters = {}; 62 63 this._childNodeCount = payload.childNodeCount || 0; 64 this._children = null; 65 66 this.nextSibling = null; 67 this.previousSibling = null; 68 this.firstChild = null; 69 this.lastChild = null; 70 this.parentNode = null; 71 72 if (payload.shadowRoots) { 73 for (var i = 0; i < payload.shadowRoots.length; ++i) { 74 var root = payload.shadowRoots[i]; 75 var node = new WebInspector.DOMNode(this._domAgent, this.ownerDocument, true, root); 76 this._shadowRoots.push(node); 77 node.parentNode = this; 78 } 79 } 80 81 if (payload.templateContent) { 82 this._templateContent = new WebInspector.DOMNode(this._domAgent, this.ownerDocument, true, payload.templateContent); 83 this._templateContent.parentNode = this; 84 } 85 86 if (payload.children) 87 this._setChildrenPayload(payload.children); 88 89 this._setPseudoElements(payload.pseudoElements); 90 91 if (payload.contentDocument) { 92 this._contentDocument = new WebInspector.DOMDocument(domAgent, payload.contentDocument); 93 this._children = [this._contentDocument]; 94 this._renumber(); 95 } 96 97 if (this._nodeType === Node.ELEMENT_NODE) { 98 // HTML and BODY from internal iframes should not overwrite top-level ones. 99 if (this.ownerDocument && !this.ownerDocument.documentElement && this._nodeName === "HTML") 100 this.ownerDocument.documentElement = this; 101 if (this.ownerDocument && !this.ownerDocument.body && this._nodeName === "BODY") 102 this.ownerDocument.body = this; 103 } else if (this._nodeType === Node.DOCUMENT_TYPE_NODE) { 104 this.publicId = payload.publicId; 105 this.systemId = payload.systemId; 106 this.internalSubset = payload.internalSubset; 107 } else if (this._nodeType === Node.ATTRIBUTE_NODE) { 108 this.name = payload.name; 109 this.value = payload.value; 110 } 111 } 112 113 WebInspector.DOMNode.PseudoElementNames = { 114 Before: "before", 115 After: "after" 116 } 117 118 WebInspector.DOMNode.ShadowRootTypes = { 119 UserAgent: "user-agent", 120 Author: "author" 121 } 122 123 WebInspector.DOMNode.prototype = { 124 /** 125 * @return {?Array.<!WebInspector.DOMNode>} 126 */ 127 children: function() 128 { 129 return this._children ? this._children.slice() : null; 130 }, 131 132 /** 133 * @return {boolean} 134 */ 135 hasAttributes: function() 136 { 137 return this._attributes.length > 0; 138 }, 139 140 /** 141 * @return {number} 142 */ 143 childNodeCount: function() 144 { 145 return this._childNodeCount; 146 }, 147 148 /** 149 * @return {boolean} 150 */ 151 hasShadowRoots: function() 152 { 153 return !!this._shadowRoots.length; 154 }, 155 156 /** 157 * @return {!Array.<!WebInspector.DOMNode>} 158 */ 159 shadowRoots: function() 160 { 161 return this._shadowRoots.slice(); 162 }, 163 164 /** 165 * @return {!WebInspector.DOMNode} 166 */ 167 templateContent: function() 168 { 169 return this._templateContent; 170 }, 171 172 /** 173 * @return {number} 174 */ 175 nodeType: function() 176 { 177 return this._nodeType; 178 }, 179 180 /** 181 * @return {string} 182 */ 183 nodeName: function() 184 { 185 return this._nodeName; 186 }, 187 188 /** 189 * @return {string|undefined} 190 */ 191 pseudoType: function() 192 { 193 return this._pseudoType; 194 }, 195 196 /** 197 * @return {boolean} 198 */ 199 hasPseudoElements: function() 200 { 201 return Object.keys(this._pseudoElements).length !== 0; 202 }, 203 204 /** 205 * @return {!Object.<string, !WebInspector.DOMNode>} 206 */ 207 pseudoElements: function() 208 { 209 return this._pseudoElements; 210 }, 211 212 /** 213 * @return {boolean} 214 */ 215 isInShadowTree: function() 216 { 217 return this._isInShadowTree; 218 }, 219 220 /** 221 * @return {?string} 222 */ 223 shadowRootType: function() 224 { 225 return this._shadowRootType || null; 226 }, 227 228 /** 229 * @return {string} 230 */ 231 nodeNameInCorrectCase: function() 232 { 233 return this.isXMLNode() ? this.nodeName() : this.nodeName().toLowerCase(); 234 }, 235 236 /** 237 * @param {string} name 238 * @param {function(?Protocol.Error)=} callback 239 */ 240 setNodeName: function(name, callback) 241 { 242 DOMAgent.setNodeName(this.id, name, WebInspector.domAgent._markRevision(this, callback)); 243 }, 244 245 /** 246 * @return {string} 247 */ 248 localName: function() 249 { 250 return this._localName; 251 }, 252 253 /** 254 * @return {string} 255 */ 256 nodeValue: function() 257 { 258 return this._nodeValue; 259 }, 260 261 /** 262 * @param {string} value 263 * @param {function(?Protocol.Error)=} callback 264 */ 265 setNodeValue: function(value, callback) 266 { 267 DOMAgent.setNodeValue(this.id, value, WebInspector.domAgent._markRevision(this, callback)); 268 }, 269 270 /** 271 * @param {string} name 272 * @return {string} 273 */ 274 getAttribute: function(name) 275 { 276 var attr = this._attributesMap[name]; 277 return attr ? attr.value : undefined; 278 }, 279 280 /** 281 * @param {string} name 282 * @param {string} text 283 * @param {function(?Protocol.Error)=} callback 284 */ 285 setAttribute: function(name, text, callback) 286 { 287 DOMAgent.setAttributesAsText(this.id, text, name, WebInspector.domAgent._markRevision(this, callback)); 288 }, 289 290 /** 291 * @param {string} name 292 * @param {string} value 293 * @param {function(?Protocol.Error)=} callback 294 */ 295 setAttributeValue: function(name, value, callback) 296 { 297 DOMAgent.setAttributeValue(this.id, name, value, WebInspector.domAgent._markRevision(this, callback)); 298 }, 299 300 /** 301 * @return {!Object} 302 */ 303 attributes: function() 304 { 305 return this._attributes; 306 }, 307 308 /** 309 * @param {string} name 310 * @param {function(?Protocol.Error)=} callback 311 */ 312 removeAttribute: function(name, callback) 313 { 314 /** 315 * @param {?Protocol.Error} error 316 * @this {WebInspector.DOMNode} 317 */ 318 function mycallback(error) 319 { 320 if (!error) { 321 delete this._attributesMap[name]; 322 for (var i = 0; i < this._attributes.length; ++i) { 323 if (this._attributes[i].name === name) { 324 this._attributes.splice(i, 1); 325 break; 326 } 327 } 328 } 329 330 WebInspector.domAgent._markRevision(this, callback)(error); 331 } 332 DOMAgent.removeAttribute(this.id, name, mycallback.bind(this)); 333 }, 334 335 /** 336 * @param {function(?Array.<!WebInspector.DOMNode>)=} callback 337 */ 338 getChildNodes: function(callback) 339 { 340 if (this._children) { 341 if (callback) 342 callback(this.children()); 343 return; 344 } 345 346 /** 347 * @this {WebInspector.DOMNode} 348 * @param {?Protocol.Error} error 349 */ 350 function mycallback(error) 351 { 352 if (callback) 353 callback(error ? null : this.children()); 354 } 355 356 DOMAgent.requestChildNodes(this.id, undefined, mycallback.bind(this)); 357 }, 358 359 /** 360 * @param {number} depth 361 * @param {function(?Array.<!WebInspector.DOMNode>)=} callback 362 */ 363 getSubtree: function(depth, callback) 364 { 365 /** 366 * @this {WebInspector.DOMNode} 367 * @param {?Protocol.Error} error 368 */ 369 function mycallback(error) 370 { 371 if (callback) 372 callback(error ? null : this._children); 373 } 374 375 DOMAgent.requestChildNodes(this.id, depth, mycallback.bind(this)); 376 }, 377 378 /** 379 * @param {function(?Protocol.Error)=} callback 380 */ 381 getOuterHTML: function(callback) 382 { 383 DOMAgent.getOuterHTML(this.id, callback); 384 }, 385 386 /** 387 * @param {string} html 388 * @param {function(?Protocol.Error)=} callback 389 */ 390 setOuterHTML: function(html, callback) 391 { 392 DOMAgent.setOuterHTML(this.id, html, WebInspector.domAgent._markRevision(this, callback)); 393 }, 394 395 /** 396 * @param {function(?Protocol.Error, !DOMAgent.NodeId=)=} callback 397 */ 398 removeNode: function(callback) 399 { 400 DOMAgent.removeNode(this.id, WebInspector.domAgent._markRevision(this, callback)); 401 }, 402 403 copyNode: function() 404 { 405 function copy(error, text) 406 { 407 if (!error) 408 InspectorFrontendHost.copyText(text); 409 } 410 DOMAgent.getOuterHTML(this.id, copy); 411 }, 412 413 /** 414 * @param {string} objectGroupId 415 * @param {function(?Protocol.Error)=} callback 416 */ 417 eventListeners: function(objectGroupId, callback) 418 { 419 DOMAgent.getEventListenersForNode(this.id, objectGroupId, callback); 420 }, 421 422 /** 423 * @return {string} 424 */ 425 path: function() 426 { 427 var path = []; 428 var node = this; 429 while (node && "index" in node && node._nodeName.length) { 430 path.push([node.index, node._nodeName]); 431 node = node.parentNode; 432 } 433 path.reverse(); 434 return path.join(","); 435 }, 436 437 /** 438 * @param {!WebInspector.DOMNode} node 439 * @return {boolean} 440 */ 441 isAncestor: function(node) 442 { 443 if (!node) 444 return false; 445 446 var currentNode = node.parentNode; 447 while (currentNode) { 448 if (this === currentNode) 449 return true; 450 currentNode = currentNode.parentNode; 451 } 452 return false; 453 }, 454 455 /** 456 * @param {!WebInspector.DOMNode} descendant 457 * @return {boolean} 458 */ 459 isDescendant: function(descendant) 460 { 461 return descendant !== null && descendant.isAncestor(this); 462 }, 463 464 /** 465 * @param {!Array.<string>} attrs 466 * @return {boolean} 467 */ 468 _setAttributesPayload: function(attrs) 469 { 470 var attributesChanged = !this._attributes || attrs.length !== this._attributes.length * 2; 471 var oldAttributesMap = this._attributesMap || {}; 472 473 this._attributes = []; 474 this._attributesMap = {}; 475 476 for (var i = 0; i < attrs.length; i += 2) { 477 var name = attrs[i]; 478 var value = attrs[i + 1]; 479 this._addAttribute(name, value); 480 481 if (attributesChanged) 482 continue; 483 484 if (!oldAttributesMap[name] || oldAttributesMap[name].value !== value) 485 attributesChanged = true; 486 } 487 return attributesChanged; 488 }, 489 490 /** 491 * @param {!WebInspector.DOMNode} prev 492 * @param {!DOMAgent.Node} payload 493 * @return {!WebInspector.DOMNode} 494 */ 495 _insertChild: function(prev, payload) 496 { 497 var node = new WebInspector.DOMNode(this._domAgent, this.ownerDocument, this._isInShadowTree, payload); 498 this._children.splice(this._children.indexOf(prev) + 1, 0, node); 499 this._renumber(); 500 return node; 501 }, 502 503 /** 504 * @param {!WebInspector.DOMNode} node 505 */ 506 _removeChild: function(node) 507 { 508 if (node.pseudoType()) { 509 delete this._pseudoElements[node.pseudoType()]; 510 } else { 511 var shadowRootIndex = this._shadowRoots.indexOf(node); 512 if (shadowRootIndex !== -1) 513 this._shadowRoots.splice(shadowRootIndex, 1); 514 else 515 this._children.splice(this._children.indexOf(node), 1); 516 } 517 node.parentNode = null; 518 node._updateChildUserPropertyCountsOnRemoval(this); 519 this._renumber(); 520 }, 521 522 /** 523 * @param {!Array.<!DOMAgent.Node>} payloads 524 */ 525 _setChildrenPayload: function(payloads) 526 { 527 // We set children in the constructor. 528 if (this._contentDocument) 529 return; 530 531 this._children = []; 532 for (var i = 0; i < payloads.length; ++i) { 533 var payload = payloads[i]; 534 var node = new WebInspector.DOMNode(this._domAgent, this.ownerDocument, this._isInShadowTree, payload); 535 this._children.push(node); 536 } 537 this._renumber(); 538 }, 539 540 /** 541 * @param {!Array.<!DOMAgent.Node>|undefined} payloads 542 */ 543 _setPseudoElements: function(payloads) 544 { 545 this._pseudoElements = {}; 546 if (!payloads) 547 return; 548 549 for (var i = 0; i < payloads.length; ++i) { 550 var node = new WebInspector.DOMNode(this._domAgent, this.ownerDocument, this._isInShadowTree, payloads[i]); 551 node.parentNode = this; 552 this._pseudoElements[node.pseudoType()] = node; 553 } 554 }, 555 556 _renumber: function() 557 { 558 this._childNodeCount = this._children.length; 559 if (this._childNodeCount == 0) { 560 this.firstChild = null; 561 this.lastChild = null; 562 return; 563 } 564 this.firstChild = this._children[0]; 565 this.lastChild = this._children[this._childNodeCount - 1]; 566 for (var i = 0; i < this._childNodeCount; ++i) { 567 var child = this._children[i]; 568 child.index = i; 569 child.nextSibling = i + 1 < this._childNodeCount ? this._children[i + 1] : null; 570 child.previousSibling = i - 1 >= 0 ? this._children[i - 1] : null; 571 child.parentNode = this; 572 } 573 }, 574 575 /** 576 * @param {string} name 577 * @param {string} value 578 */ 579 _addAttribute: function(name, value) 580 { 581 var attr = { 582 name: name, 583 value: value, 584 _node: this 585 }; 586 this._attributesMap[name] = attr; 587 this._attributes.push(attr); 588 }, 589 590 /** 591 * @param {string} name 592 * @param {string} value 593 */ 594 _setAttribute: function(name, value) 595 { 596 var attr = this._attributesMap[name]; 597 if (attr) 598 attr.value = value; 599 else 600 this._addAttribute(name, value); 601 }, 602 603 /** 604 * @param {string} name 605 */ 606 _removeAttribute: function(name) 607 { 608 var attr = this._attributesMap[name]; 609 if (attr) { 610 this._attributes.remove(attr); 611 delete this._attributesMap[name]; 612 } 613 }, 614 615 /** 616 * @param {!WebInspector.DOMNode} targetNode 617 * @param {?WebInspector.DOMNode} anchorNode 618 * @param {function(?Protocol.Error, !DOMAgent.NodeId=)=} callback 619 */ 620 moveTo: function(targetNode, anchorNode, callback) 621 { 622 DOMAgent.moveTo(this.id, targetNode.id, anchorNode ? anchorNode.id : undefined, WebInspector.domAgent._markRevision(this, callback)); 623 }, 624 625 /** 626 * @return {boolean} 627 */ 628 isXMLNode: function() 629 { 630 return !!this.ownerDocument && !!this.ownerDocument.xmlVersion; 631 }, 632 633 _updateChildUserPropertyCountsOnRemoval: function(parentNode) 634 { 635 var result = {}; 636 if (this._userProperties) { 637 for (var name in this._userProperties) 638 result[name] = (result[name] || 0) + 1; 639 } 640 641 if (this._descendantUserPropertyCounters) { 642 for (var name in this._descendantUserPropertyCounters) { 643 var counter = this._descendantUserPropertyCounters[name]; 644 result[name] = (result[name] || 0) + counter; 645 } 646 } 647 648 for (var name in result) 649 parentNode._updateDescendantUserPropertyCount(name, -result[name]); 650 }, 651 652 _updateDescendantUserPropertyCount: function(name, delta) 653 { 654 if (!this._descendantUserPropertyCounters.hasOwnProperty(name)) 655 this._descendantUserPropertyCounters[name] = 0; 656 this._descendantUserPropertyCounters[name] += delta; 657 if (!this._descendantUserPropertyCounters[name]) 658 delete this._descendantUserPropertyCounters[name]; 659 if (this.parentNode) 660 this.parentNode._updateDescendantUserPropertyCount(name, delta); 661 }, 662 663 setUserProperty: function(name, value) 664 { 665 if (value === null) { 666 this.removeUserProperty(name); 667 return; 668 } 669 670 if (this.parentNode && !this._userProperties.hasOwnProperty(name)) 671 this.parentNode._updateDescendantUserPropertyCount(name, 1); 672 673 this._userProperties[name] = value; 674 }, 675 676 removeUserProperty: function(name) 677 { 678 if (!this._userProperties.hasOwnProperty(name)) 679 return; 680 681 delete this._userProperties[name]; 682 if (this.parentNode) 683 this.parentNode._updateDescendantUserPropertyCount(name, -1); 684 }, 685 686 getUserProperty: function(name) 687 { 688 return this._userProperties ? this._userProperties[name] : null; 689 }, 690 691 descendantUserPropertyCount: function(name) 692 { 693 return this._descendantUserPropertyCounters && this._descendantUserPropertyCounters[name] ? this._descendantUserPropertyCounters[name] : 0; 694 }, 695 696 /** 697 * @param {string} url 698 * @return {?string} 699 */ 700 resolveURL: function(url) 701 { 702 if (!url) 703 return url; 704 for (var frameOwnerCandidate = this; frameOwnerCandidate; frameOwnerCandidate = frameOwnerCandidate.parentNode) { 705 if (frameOwnerCandidate.baseURL) 706 return WebInspector.ParsedURL.completeURL(frameOwnerCandidate.baseURL, url); 707 } 708 return null; 709 } 710 } 711 712 /** 713 * @extends {WebInspector.DOMNode} 714 * @constructor 715 * @param {!WebInspector.DOMAgent} domAgent 716 * @param {!DOMAgent.Node} payload 717 */ 718 WebInspector.DOMDocument = function(domAgent, payload) 719 { 720 WebInspector.DOMNode.call(this, domAgent, this, false, payload); 721 this.documentURL = payload.documentURL || ""; 722 this.baseURL = payload.baseURL || ""; 723 this.xmlVersion = payload.xmlVersion; 724 this._listeners = {}; 725 } 726 727 WebInspector.DOMDocument.prototype = { 728 __proto__: WebInspector.DOMNode.prototype 729 } 730 731 /** 732 * @extends {WebInspector.Object} 733 * @constructor 734 */ 735 WebInspector.DOMAgent = function() { 736 /** @type {!Object.<number, !WebInspector.DOMNode>} */ 737 this._idToDOMNode = {}; 738 /** @type {?WebInspector.DOMDocument} */ 739 this._document = null; 740 /** @type {!Object.<number, boolean>} */ 741 this._attributeLoadNodeIds = {}; 742 InspectorBackend.registerDOMDispatcher(new WebInspector.DOMDispatcher(this)); 743 744 this._defaultHighlighter = new WebInspector.DefaultDOMNodeHighlighter(); 745 this._highlighter = this._defaultHighlighter; 746 } 747 748 WebInspector.DOMAgent.Events = { 749 AttrModified: "AttrModified", 750 AttrRemoved: "AttrRemoved", 751 CharacterDataModified: "CharacterDataModified", 752 NodeInserted: "NodeInserted", 753 NodeRemoved: "NodeRemoved", 754 DocumentUpdated: "DocumentUpdated", 755 ChildNodeCountUpdated: "ChildNodeCountUpdated", 756 UndoRedoRequested: "UndoRedoRequested", 757 UndoRedoCompleted: "UndoRedoCompleted", 758 InspectNodeRequested: "InspectNodeRequested" 759 } 760 761 WebInspector.DOMAgent.prototype = { 762 /** 763 * @param {function(!WebInspector.DOMDocument)=} callback 764 */ 765 requestDocument: function(callback) 766 { 767 if (this._document) { 768 if (callback) 769 callback(this._document); 770 return; 771 } 772 773 if (this._pendingDocumentRequestCallbacks) { 774 this._pendingDocumentRequestCallbacks.push(callback); 775 return; 776 } 777 778 this._pendingDocumentRequestCallbacks = [callback]; 779 780 /** 781 * @this {WebInspector.DOMAgent} 782 * @param {?Protocol.Error} error 783 * @param {!DOMAgent.Node} root 784 */ 785 function onDocumentAvailable(error, root) 786 { 787 if (!error) 788 this._setDocument(root); 789 790 for (var i = 0; i < this._pendingDocumentRequestCallbacks.length; ++i) { 791 var callback = this._pendingDocumentRequestCallbacks[i]; 792 if (callback) 793 callback(this._document); 794 } 795 delete this._pendingDocumentRequestCallbacks; 796 } 797 798 DOMAgent.getDocument(onDocumentAvailable.bind(this)); 799 }, 800 801 /** 802 * @return {?WebInspector.DOMDocument} 803 */ 804 existingDocument: function() 805 { 806 return this._document; 807 }, 808 809 /** 810 * @param {!RuntimeAgent.RemoteObjectId} objectId 811 * @param {function(?DOMAgent.NodeId)=} callback 812 */ 813 pushNodeToFrontend: function(objectId, callback) 814 { 815 this._dispatchWhenDocumentAvailable(DOMAgent.requestNode.bind(DOMAgent, objectId), callback); 816 }, 817 818 /** 819 * @param {string} path 820 * @param {function(?number)=} callback 821 */ 822 pushNodeByPathToFrontend: function(path, callback) 823 { 824 this._dispatchWhenDocumentAvailable(DOMAgent.pushNodeByPathToFrontend.bind(DOMAgent, path), callback); 825 }, 826 827 /** 828 * @param {number} backendNodeId 829 * @param {function(?number)=} callback 830 */ 831 pushNodeByBackendIdToFrontend: function(backendNodeId, callback) 832 { 833 this._dispatchWhenDocumentAvailable(DOMAgent.pushNodeByBackendIdToFrontend.bind(DOMAgent, backendNodeId), callback); 834 }, 835 836 /** 837 * @param {function(!T)=} callback 838 * @return {function(?Protocol.Error, !T=)|undefined} 839 * @template T 840 */ 841 _wrapClientCallback: function(callback) 842 { 843 if (!callback) 844 return; 845 /** 846 * @param {?Protocol.Error} error 847 * @param {!T=} result 848 * @template T 849 */ 850 return function(error, result) 851 { 852 // Caller is responsible for handling the actual error. 853 callback(error ? null : result); 854 } 855 }, 856 857 /** 858 * @param {function(function(?Protocol.Error, !T=)=)} func 859 * @param {function(!T)=} callback 860 * @template T 861 */ 862 _dispatchWhenDocumentAvailable: function(func, callback) 863 { 864 var callbackWrapper = this._wrapClientCallback(callback); 865 866 /** 867 * @this {WebInspector.DOMAgent} 868 */ 869 function onDocumentAvailable() 870 { 871 if (this._document) 872 func(callbackWrapper); 873 else { 874 if (callbackWrapper) 875 callbackWrapper("No document"); 876 } 877 } 878 this.requestDocument(onDocumentAvailable.bind(this)); 879 }, 880 881 /** 882 * @param {!DOMAgent.NodeId} nodeId 883 * @param {string} name 884 * @param {string} value 885 */ 886 _attributeModified: function(nodeId, name, value) 887 { 888 var node = this._idToDOMNode[nodeId]; 889 if (!node) 890 return; 891 892 node._setAttribute(name, value); 893 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.AttrModified, { node: node, name: name }); 894 }, 895 896 /** 897 * @param {!DOMAgent.NodeId} nodeId 898 * @param {string} name 899 */ 900 _attributeRemoved: function(nodeId, name) 901 { 902 var node = this._idToDOMNode[nodeId]; 903 if (!node) 904 return; 905 node._removeAttribute(name); 906 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.AttrRemoved, { node: node, name: name }); 907 }, 908 909 /** 910 * @param {!Array.<!DOMAgent.NodeId>} nodeIds 911 */ 912 _inlineStyleInvalidated: function(nodeIds) 913 { 914 for (var i = 0; i < nodeIds.length; ++i) 915 this._attributeLoadNodeIds[nodeIds[i]] = true; 916 if ("_loadNodeAttributesTimeout" in this) 917 return; 918 this._loadNodeAttributesTimeout = setTimeout(this._loadNodeAttributes.bind(this), 0); 919 }, 920 921 _loadNodeAttributes: function() 922 { 923 /** 924 * @this {WebInspector.DOMAgent} 925 * @param {!DOMAgent.NodeId} nodeId 926 * @param {?Protocol.Error} error 927 * @param {!Array.<string>} attributes 928 */ 929 function callback(nodeId, error, attributes) 930 { 931 if (error) { 932 // We are calling _loadNodeAttributes asynchronously, it is ok if node is not found. 933 return; 934 } 935 var node = this._idToDOMNode[nodeId]; 936 if (node) { 937 if (node._setAttributesPayload(attributes)) 938 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.AttrModified, { node: node, name: "style" }); 939 } 940 } 941 942 delete this._loadNodeAttributesTimeout; 943 944 for (var nodeId in this._attributeLoadNodeIds) { 945 var nodeIdAsNumber = parseInt(nodeId, 10); 946 DOMAgent.getAttributes(nodeIdAsNumber, callback.bind(this, nodeIdAsNumber)); 947 } 948 this._attributeLoadNodeIds = {}; 949 }, 950 951 /** 952 * @param {!DOMAgent.NodeId} nodeId 953 * @param {string} newValue 954 */ 955 _characterDataModified: function(nodeId, newValue) 956 { 957 var node = this._idToDOMNode[nodeId]; 958 node._nodeValue = newValue; 959 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.CharacterDataModified, node); 960 }, 961 962 /** 963 * @param {!DOMAgent.NodeId} nodeId 964 * @return {?WebInspector.DOMNode} 965 */ 966 nodeForId: function(nodeId) 967 { 968 return this._idToDOMNode[nodeId] || null; 969 }, 970 971 _documentUpdated: function() 972 { 973 this._setDocument(null); 974 }, 975 976 /** 977 * @param {?DOMAgent.Node} payload 978 */ 979 _setDocument: function(payload) 980 { 981 this._idToDOMNode = {}; 982 if (payload && "nodeId" in payload) 983 this._document = new WebInspector.DOMDocument(this, payload); 984 else 985 this._document = null; 986 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.DocumentUpdated, this._document); 987 }, 988 989 /** 990 * @param {!DOMAgent.Node} payload 991 */ 992 _setDetachedRoot: function(payload) 993 { 994 if (payload.nodeName === "#document") 995 new WebInspector.DOMDocument(this, payload); 996 else 997 new WebInspector.DOMNode(this, null, false, payload); 998 }, 999 1000 /** 1001 * @param {!DOMAgent.NodeId} parentId 1002 * @param {!Array.<!DOMAgent.Node>} payloads 1003 */ 1004 _setChildNodes: function(parentId, payloads) 1005 { 1006 if (!parentId && payloads.length) { 1007 this._setDetachedRoot(payloads[0]); 1008 return; 1009 } 1010 1011 var parent = this._idToDOMNode[parentId]; 1012 parent._setChildrenPayload(payloads); 1013 }, 1014 1015 /** 1016 * @param {!DOMAgent.NodeId} nodeId 1017 * @param {number} newValue 1018 */ 1019 _childNodeCountUpdated: function(nodeId, newValue) 1020 { 1021 var node = this._idToDOMNode[nodeId]; 1022 node._childNodeCount = newValue; 1023 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.ChildNodeCountUpdated, node); 1024 }, 1025 1026 /** 1027 * @param {!DOMAgent.NodeId} parentId 1028 * @param {!DOMAgent.NodeId} prevId 1029 * @param {!DOMAgent.Node} payload 1030 */ 1031 _childNodeInserted: function(parentId, prevId, payload) 1032 { 1033 var parent = this._idToDOMNode[parentId]; 1034 var prev = this._idToDOMNode[prevId]; 1035 var node = parent._insertChild(prev, payload); 1036 this._idToDOMNode[node.id] = node; 1037 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.NodeInserted, node); 1038 }, 1039 1040 /** 1041 * @param {!DOMAgent.NodeId} parentId 1042 * @param {!DOMAgent.NodeId} nodeId 1043 */ 1044 _childNodeRemoved: function(parentId, nodeId) 1045 { 1046 var parent = this._idToDOMNode[parentId]; 1047 var node = this._idToDOMNode[nodeId]; 1048 parent._removeChild(node); 1049 this._unbind(node); 1050 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.NodeRemoved, {node: node, parent: parent}); 1051 }, 1052 1053 /** 1054 * @param {!DOMAgent.NodeId} hostId 1055 * @param {!DOMAgent.Node} root 1056 */ 1057 _shadowRootPushed: function(hostId, root) 1058 { 1059 var host = this._idToDOMNode[hostId]; 1060 if (!host) 1061 return; 1062 var node = new WebInspector.DOMNode(this, host.ownerDocument, true, root); 1063 node.parentNode = host; 1064 this._idToDOMNode[node.id] = node; 1065 host._shadowRoots.push(node); 1066 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.NodeInserted, node); 1067 }, 1068 1069 /** 1070 * @param {!DOMAgent.NodeId} hostId 1071 * @param {!DOMAgent.NodeId} rootId 1072 */ 1073 _shadowRootPopped: function(hostId, rootId) 1074 { 1075 var host = this._idToDOMNode[hostId]; 1076 if (!host) 1077 return; 1078 var root = this._idToDOMNode[rootId]; 1079 if (!root) 1080 return; 1081 host._removeChild(root); 1082 this._unbind(root); 1083 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.NodeRemoved, {node: root, parent: host}); 1084 }, 1085 1086 /** 1087 * @param {!DOMAgent.NodeId} parentId 1088 * @param {!DOMAgent.Node} pseudoElement 1089 */ 1090 _pseudoElementAdded: function(parentId, pseudoElement) 1091 { 1092 var parent = this._idToDOMNode[parentId]; 1093 if (!parent) 1094 return; 1095 var node = new WebInspector.DOMNode(this, parent.ownerDocument, false, pseudoElement); 1096 node.parentNode = parent; 1097 this._idToDOMNode[node.id] = node; 1098 console.assert(!parent._pseudoElements[node.pseudoType()]); 1099 parent._pseudoElements[node.pseudoType()] = node; 1100 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.NodeInserted, node); 1101 }, 1102 1103 /** 1104 * @param {!DOMAgent.NodeId} parentId 1105 * @param {!DOMAgent.NodeId} pseudoElementId 1106 */ 1107 _pseudoElementRemoved: function(parentId, pseudoElementId) 1108 { 1109 var parent = this._idToDOMNode[parentId]; 1110 if (!parent) 1111 return; 1112 var pseudoElement = this._idToDOMNode[pseudoElementId]; 1113 if (!pseudoElement) 1114 return; 1115 parent._removeChild(pseudoElement); 1116 this._unbind(pseudoElement); 1117 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.NodeRemoved, {node: pseudoElement, parent: parent}); 1118 }, 1119 1120 /** 1121 * @param {!WebInspector.DOMNode} node 1122 */ 1123 _unbind: function(node) 1124 { 1125 delete this._idToDOMNode[node.id]; 1126 for (var i = 0; node._children && i < node._children.length; ++i) 1127 this._unbind(node._children[i]); 1128 for (var i = 0; i < node._shadowRoots.length; ++i) 1129 this._unbind(node._shadowRoots[i]); 1130 var pseudoElements = node.pseudoElements(); 1131 for (var id in pseudoElements) 1132 this._unbind(pseudoElements[id]); 1133 if (node._templateContent) 1134 this._unbind(node._templateContent); 1135 }, 1136 1137 /** 1138 * @param {number} nodeId 1139 */ 1140 inspectElement: function(nodeId) 1141 { 1142 var node = this._idToDOMNode[nodeId]; 1143 if (node) 1144 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.InspectNodeRequested, nodeId); 1145 }, 1146 1147 /** 1148 * @param {!DOMAgent.NodeId} nodeId 1149 */ 1150 _inspectNodeRequested: function(nodeId) 1151 { 1152 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.InspectNodeRequested, nodeId); 1153 }, 1154 1155 /** 1156 * @param {string} query 1157 * @param {function(number)} searchCallback 1158 */ 1159 performSearch: function(query, searchCallback) 1160 { 1161 this.cancelSearch(); 1162 1163 /** 1164 * @param {?Protocol.Error} error 1165 * @param {string} searchId 1166 * @param {number} resultsCount 1167 * @this {WebInspector.DOMAgent} 1168 */ 1169 function callback(error, searchId, resultsCount) 1170 { 1171 this._searchId = searchId; 1172 searchCallback(resultsCount); 1173 } 1174 DOMAgent.performSearch(query, callback.bind(this)); 1175 }, 1176 1177 /** 1178 * @param {number} index 1179 * @param {?function(?WebInspector.DOMNode)} callback 1180 */ 1181 searchResult: function(index, callback) 1182 { 1183 if (this._searchId) 1184 DOMAgent.getSearchResults(this._searchId, index, index + 1, searchResultsCallback.bind(this)); 1185 else 1186 callback(null); 1187 1188 /** 1189 * @param {?Protocol.Error} error 1190 * @param {!Array.<number>} nodeIds 1191 * @this {WebInspector.DOMAgent} 1192 */ 1193 function searchResultsCallback(error, nodeIds) 1194 { 1195 if (error) { 1196 console.error(error); 1197 callback(null); 1198 return; 1199 } 1200 if (nodeIds.length != 1) 1201 return; 1202 1203 callback(this.nodeForId(nodeIds[0])); 1204 } 1205 }, 1206 1207 cancelSearch: function() 1208 { 1209 if (this._searchId) { 1210 DOMAgent.discardSearchResults(this._searchId); 1211 delete this._searchId; 1212 } 1213 }, 1214 1215 /** 1216 * @param {!DOMAgent.NodeId} nodeId 1217 * @param {string} selectors 1218 * @param {function(?DOMAgent.NodeId)=} callback 1219 */ 1220 querySelector: function(nodeId, selectors, callback) 1221 { 1222 DOMAgent.querySelector(nodeId, selectors, this._wrapClientCallback(callback)); 1223 }, 1224 1225 /** 1226 * @param {!DOMAgent.NodeId} nodeId 1227 * @param {string} selectors 1228 * @param {function(!Array.<!DOMAgent.NodeId>=)=} callback 1229 */ 1230 querySelectorAll: function(nodeId, selectors, callback) 1231 { 1232 DOMAgent.querySelectorAll(nodeId, selectors, this._wrapClientCallback(callback)); 1233 }, 1234 1235 /** 1236 * @param {!DOMAgent.NodeId=} nodeId 1237 * @param {string=} mode 1238 * @param {!RuntimeAgent.RemoteObjectId=} objectId 1239 */ 1240 highlightDOMNode: function(nodeId, mode, objectId) 1241 { 1242 if (this._hideDOMNodeHighlightTimeout) { 1243 clearTimeout(this._hideDOMNodeHighlightTimeout); 1244 delete this._hideDOMNodeHighlightTimeout; 1245 } 1246 this._highlighter.highlightDOMNode(nodeId || 0, this._buildHighlightConfig(mode), objectId); 1247 }, 1248 1249 hideDOMNodeHighlight: function() 1250 { 1251 this.highlightDOMNode(0); 1252 }, 1253 1254 /** 1255 * @param {!DOMAgent.NodeId} nodeId 1256 */ 1257 highlightDOMNodeForTwoSeconds: function(nodeId) 1258 { 1259 this.highlightDOMNode(nodeId); 1260 this._hideDOMNodeHighlightTimeout = setTimeout(this.hideDOMNodeHighlight.bind(this), 2000); 1261 }, 1262 1263 /** 1264 * @param {boolean} enabled 1265 * @param {boolean} inspectShadowDOM 1266 * @param {function(?Protocol.Error)=} callback 1267 */ 1268 setInspectModeEnabled: function(enabled, inspectShadowDOM, callback) 1269 { 1270 /** 1271 * @this {WebInspector.DOMAgent} 1272 */ 1273 function onDocumentAvailable() 1274 { 1275 this._highlighter.setInspectModeEnabled(enabled, inspectShadowDOM, this._buildHighlightConfig(), callback); 1276 } 1277 this.requestDocument(onDocumentAvailable.bind(this)); 1278 }, 1279 1280 /** 1281 * @param {string=} mode 1282 * @return {!DOMAgent.HighlightConfig} 1283 */ 1284 _buildHighlightConfig: function(mode) 1285 { 1286 mode = mode || "all"; 1287 var highlightConfig = { showInfo: mode === "all", showRulers: WebInspector.settings.showMetricsRulers.get() }; 1288 if (mode === "all" || mode === "content") 1289 highlightConfig.contentColor = WebInspector.Color.PageHighlight.Content.toProtocolRGBA(); 1290 1291 if (mode === "all" || mode === "padding") 1292 highlightConfig.paddingColor = WebInspector.Color.PageHighlight.Padding.toProtocolRGBA(); 1293 1294 if (mode === "all" || mode === "border") 1295 highlightConfig.borderColor = WebInspector.Color.PageHighlight.Border.toProtocolRGBA(); 1296 1297 if (mode === "all" || mode === "margin") 1298 highlightConfig.marginColor = WebInspector.Color.PageHighlight.Margin.toProtocolRGBA(); 1299 1300 if (mode === "all") 1301 highlightConfig.eventTargetColor = WebInspector.Color.PageHighlight.EventTarget.toProtocolRGBA(); 1302 1303 return highlightConfig; 1304 }, 1305 1306 /** 1307 * @param {!WebInspector.DOMNode} node 1308 * @param {function(?Protocol.Error, !A=, !B=)=} callback 1309 * @return {function(?Protocol.Error, !A=, !B=)} 1310 * @template A,B 1311 */ 1312 _markRevision: function(node, callback) 1313 { 1314 /** 1315 * @param {?Protocol.Error} error 1316 * @this {WebInspector.DOMAgent} 1317 */ 1318 function wrapperFunction(error) 1319 { 1320 if (!error) 1321 this.markUndoableState(); 1322 1323 if (callback) 1324 callback.apply(this, arguments); 1325 } 1326 return wrapperFunction.bind(this); 1327 }, 1328 1329 /** 1330 * @param {boolean} emulationEnabled 1331 */ 1332 emulateTouchEventObjects: function(emulationEnabled) 1333 { 1334 const injectedFunction = function() { 1335 const touchEvents = ["ontouchstart", "ontouchend", "ontouchmove", "ontouchcancel"]; 1336 var recepients = [window.__proto__, document.__proto__]; 1337 for (var i = 0; i < touchEvents.length; ++i) { 1338 for (var j = 0; j < recepients.length; ++j) { 1339 if (!(touchEvents[i] in recepients[j])) 1340 Object.defineProperty(recepients[j], touchEvents[i], { value: null, writable: true, configurable: true, enumerable: true }); 1341 } 1342 } 1343 } 1344 1345 if (emulationEnabled && !this._addTouchEventsScriptInjecting) { 1346 this._addTouchEventsScriptInjecting = true; 1347 PageAgent.addScriptToEvaluateOnLoad("(" + injectedFunction.toString() + ")()", scriptAddedCallback.bind(this)); 1348 } else { 1349 if (typeof this._addTouchEventsScriptId !== "undefined") { 1350 PageAgent.removeScriptToEvaluateOnLoad(this._addTouchEventsScriptId); 1351 delete this._addTouchEventsScriptId; 1352 } 1353 } 1354 1355 /** 1356 * @param {?Protocol.Error} error 1357 * @param {string} scriptId 1358 * @this {WebInspector.DOMAgent} 1359 */ 1360 function scriptAddedCallback(error, scriptId) 1361 { 1362 delete this._addTouchEventsScriptInjecting; 1363 if (error) 1364 return; 1365 this._addTouchEventsScriptId = scriptId; 1366 } 1367 1368 PageAgent.setTouchEmulationEnabled(emulationEnabled); 1369 }, 1370 1371 markUndoableState: function() 1372 { 1373 DOMAgent.markUndoableState(); 1374 }, 1375 1376 /** 1377 * @param {function(?Protocol.Error)=} callback 1378 */ 1379 undo: function(callback) 1380 { 1381 /** 1382 * @param {?Protocol.Error} error 1383 * @this {WebInspector.DOMAgent} 1384 */ 1385 function mycallback(error) 1386 { 1387 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.UndoRedoCompleted); 1388 callback(error); 1389 } 1390 1391 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.UndoRedoRequested); 1392 DOMAgent.undo(callback); 1393 }, 1394 1395 /** 1396 * @param {function(?Protocol.Error)=} callback 1397 */ 1398 redo: function(callback) 1399 { 1400 /** 1401 * @param {?Protocol.Error} error 1402 * @this {WebInspector.DOMAgent} 1403 */ 1404 function mycallback(error) 1405 { 1406 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.UndoRedoCompleted); 1407 callback(error); 1408 } 1409 1410 this.dispatchEventToListeners(WebInspector.DOMAgent.Events.UndoRedoRequested); 1411 DOMAgent.redo(callback); 1412 }, 1413 1414 /** 1415 * @param {?WebInspector.DOMNodeHighlighter} highlighter 1416 */ 1417 setHighlighter: function(highlighter) 1418 { 1419 this._highlighter = highlighter || this._defaultHighlighter; 1420 }, 1421 1422 __proto__: WebInspector.Object.prototype 1423 } 1424 1425 /** 1426 * @constructor 1427 * @implements {DOMAgent.Dispatcher} 1428 * @param {!WebInspector.DOMAgent} domAgent 1429 */ 1430 WebInspector.DOMDispatcher = function(domAgent) 1431 { 1432 this._domAgent = domAgent; 1433 } 1434 1435 WebInspector.DOMDispatcher.prototype = { 1436 documentUpdated: function() 1437 { 1438 this._domAgent._documentUpdated(); 1439 }, 1440 1441 /** 1442 * @param {!DOMAgent.NodeId} nodeId 1443 */ 1444 inspectNodeRequested: function(nodeId) 1445 { 1446 this._domAgent._inspectNodeRequested(nodeId); 1447 }, 1448 1449 /** 1450 * @param {!DOMAgent.NodeId} nodeId 1451 * @param {string} name 1452 * @param {string} value 1453 */ 1454 attributeModified: function(nodeId, name, value) 1455 { 1456 this._domAgent._attributeModified(nodeId, name, value); 1457 }, 1458 1459 /** 1460 * @param {!DOMAgent.NodeId} nodeId 1461 * @param {string} name 1462 */ 1463 attributeRemoved: function(nodeId, name) 1464 { 1465 this._domAgent._attributeRemoved(nodeId, name); 1466 }, 1467 1468 /** 1469 * @param {!Array.<!DOMAgent.NodeId>} nodeIds 1470 */ 1471 inlineStyleInvalidated: function(nodeIds) 1472 { 1473 this._domAgent._inlineStyleInvalidated(nodeIds); 1474 }, 1475 1476 /** 1477 * @param {!DOMAgent.NodeId} nodeId 1478 * @param {string} characterData 1479 */ 1480 characterDataModified: function(nodeId, characterData) 1481 { 1482 this._domAgent._characterDataModified(nodeId, characterData); 1483 }, 1484 1485 /** 1486 * @param {!DOMAgent.NodeId} parentId 1487 * @param {!Array.<!DOMAgent.Node>} payloads 1488 */ 1489 setChildNodes: function(parentId, payloads) 1490 { 1491 this._domAgent._setChildNodes(parentId, payloads); 1492 }, 1493 1494 /** 1495 * @param {!DOMAgent.NodeId} nodeId 1496 * @param {number} childNodeCount 1497 */ 1498 childNodeCountUpdated: function(nodeId, childNodeCount) 1499 { 1500 this._domAgent._childNodeCountUpdated(nodeId, childNodeCount); 1501 }, 1502 1503 /** 1504 * @param {!DOMAgent.NodeId} parentNodeId 1505 * @param {!DOMAgent.NodeId} previousNodeId 1506 * @param {!DOMAgent.Node} payload 1507 */ 1508 childNodeInserted: function(parentNodeId, previousNodeId, payload) 1509 { 1510 this._domAgent._childNodeInserted(parentNodeId, previousNodeId, payload); 1511 }, 1512 1513 /** 1514 * @param {!DOMAgent.NodeId} parentNodeId 1515 * @param {!DOMAgent.NodeId} nodeId 1516 */ 1517 childNodeRemoved: function(parentNodeId, nodeId) 1518 { 1519 this._domAgent._childNodeRemoved(parentNodeId, nodeId); 1520 }, 1521 1522 /** 1523 * @param {!DOMAgent.NodeId} hostId 1524 * @param {!DOMAgent.Node} root 1525 */ 1526 shadowRootPushed: function(hostId, root) 1527 { 1528 this._domAgent._shadowRootPushed(hostId, root); 1529 }, 1530 1531 /** 1532 * @param {!DOMAgent.NodeId} hostId 1533 * @param {!DOMAgent.NodeId} rootId 1534 */ 1535 shadowRootPopped: function(hostId, rootId) 1536 { 1537 this._domAgent._shadowRootPopped(hostId, rootId); 1538 }, 1539 1540 /** 1541 * @param {!DOMAgent.NodeId} parentId 1542 * @param {!DOMAgent.Node} pseudoElement 1543 */ 1544 pseudoElementAdded: function(parentId, pseudoElement) 1545 { 1546 this._domAgent._pseudoElementAdded(parentId, pseudoElement); 1547 }, 1548 1549 /** 1550 * @param {!DOMAgent.NodeId} parentId 1551 * @param {!DOMAgent.NodeId} pseudoElementId 1552 */ 1553 pseudoElementRemoved: function(parentId, pseudoElementId) 1554 { 1555 this._domAgent._pseudoElementRemoved(parentId, pseudoElementId); 1556 } 1557 } 1558 1559 /** 1560 * @interface 1561 */ 1562 WebInspector.DOMNodeHighlighter = function() { 1563 } 1564 1565 WebInspector.DOMNodeHighlighter.prototype = { 1566 /** 1567 * @param {!DOMAgent.NodeId} nodeId 1568 * @param {!DOMAgent.HighlightConfig} config 1569 * @param {!RuntimeAgent.RemoteObjectId=} objectId 1570 */ 1571 highlightDOMNode: function(nodeId, config, objectId) {}, 1572 1573 /** 1574 * @param {boolean} enabled 1575 * @param {boolean} inspectShadowDOM 1576 * @param {!DOMAgent.HighlightConfig} config 1577 * @param {function(?Protocol.Error)=} callback 1578 */ 1579 setInspectModeEnabled: function(enabled, inspectShadowDOM, config, callback) {} 1580 } 1581 1582 /** 1583 * @constructor 1584 * @implements {WebInspector.DOMNodeHighlighter} 1585 */ 1586 WebInspector.DefaultDOMNodeHighlighter = function() { 1587 } 1588 1589 WebInspector.DefaultDOMNodeHighlighter.prototype = { 1590 /** 1591 * @param {!DOMAgent.NodeId} nodeId 1592 * @param {!DOMAgent.HighlightConfig} config 1593 * @param {!RuntimeAgent.RemoteObjectId=} objectId 1594 */ 1595 highlightDOMNode: function(nodeId, config, objectId) 1596 { 1597 if (objectId || nodeId) 1598 DOMAgent.highlightNode(config, objectId ? undefined : nodeId, objectId); 1599 else 1600 DOMAgent.hideHighlight(); 1601 }, 1602 1603 /** 1604 * @param {boolean} enabled 1605 * @param {boolean} inspectShadowDOM 1606 * @param {!DOMAgent.HighlightConfig} config 1607 * @param {function(?Protocol.Error)=} callback 1608 */ 1609 setInspectModeEnabled: function(enabled, inspectShadowDOM, config, callback) 1610 { 1611 DOMAgent.setInspectModeEnabled(enabled, inspectShadowDOM, config, callback); 1612 } 1613 } 1614 1615 /** 1616 * @type {!WebInspector.DOMAgent} 1617 */ 1618 WebInspector.domAgent; 1619