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