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