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