| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 /** | |
| 6 * @fileoverview Dom and DomNode are used to represent remote DOM in the | |
| 7 * web inspector. | |
| 8 */ | |
| 9 goog.provide('devtools.DomAgent'); | |
| 10 goog.provide('devtools.DomDocument'); | |
| 11 goog.provide('devtools.DomNode'); | |
| 12 | |
| 13 goog.require('devtools.Callback'); | |
| 14 | |
| 15 | |
| 16 /** | |
| 17 * Defines indexes for the node payload properties. | |
| 18 */ | |
| 19 devtools.PayloadIndex = { | |
| 20 ID : 0, | |
| 21 TYPE : 1, | |
| 22 NAME : 2, | |
| 23 VALUE : 3, | |
| 24 ATTRS : 4, | |
| 25 HAS_CHILDREN : 5, | |
| 26 CHILD_NODES : 6 | |
| 27 }; | |
| 28 | |
| 29 | |
| 30 /** | |
| 31 * Creates document node in a given document based on a given payload data. | |
| 32 * @param {devtools.Doc} doc Document to create node in. | |
| 33 * @param {Array.<Object>} payload Data to build node based upon. | |
| 34 * @constructor | |
| 35 */ | |
| 36 devtools.DomNode = function(doc, payload) { | |
| 37 this.ownerDocument = doc; | |
| 38 | |
| 39 this.id_ = payload[devtools.PayloadIndex.ID]; | |
| 40 this.nodeType = payload[devtools.PayloadIndex.TYPE]; | |
| 41 this.nodeName = payload[devtools.PayloadIndex.NAME]; | |
| 42 this.nodeValue_ = payload[devtools.PayloadIndex.VALUE]; | |
| 43 this.textContent = this.nodeValue; | |
| 44 | |
| 45 this.attributes = []; | |
| 46 this.attributesMap_ = {}; | |
| 47 if (payload.length > devtools.PayloadIndex.ATTRS) { | |
| 48 this.setAttributesPayload_(payload[devtools.PayloadIndex.ATTRS]); | |
| 49 } | |
| 50 | |
| 51 this.childNodesCount_ = payload[devtools.PayloadIndex.HAS_CHILDREN]; | |
| 52 this.children = null; | |
| 53 | |
| 54 this.nextSibling = null; | |
| 55 this.prevSibling = null; | |
| 56 this.firstChild = null; | |
| 57 this.parentNode = null; | |
| 58 | |
| 59 this.disabledStyleProperties_ = {}; | |
| 60 | |
| 61 if (payload.length > devtools.PayloadIndex.CHILD_NODES) { | |
| 62 // Has children payloads | |
| 63 this.setChildrenPayload_( | |
| 64 payload[devtools.PayloadIndex.CHILD_NODES]); | |
| 65 } | |
| 66 | |
| 67 this.computedStyle_ = null; | |
| 68 this.style = null; | |
| 69 this.matchedCSSRules_ = []; | |
| 70 }; | |
| 71 | |
| 72 | |
| 73 /** | |
| 74 * Overrides for getters and setters. | |
| 75 */ | |
| 76 devtools.DomNode.prototype = { | |
| 77 get nodeValue() { | |
| 78 return this.nodeValue_; | |
| 79 }, | |
| 80 | |
| 81 set nodeValue(value) { | |
| 82 if (this.nodeType != Node.TEXT_NODE) { | |
| 83 return; | |
| 84 } | |
| 85 var self = this; | |
| 86 this.ownerDocument.domAgent_.setTextNodeValueAsync(this, value, | |
| 87 function() { | |
| 88 self.nodeValue_ = value; | |
| 89 self.textContent = value; | |
| 90 }); | |
| 91 } | |
| 92 }; | |
| 93 | |
| 94 | |
| 95 /** | |
| 96 * Sets attributes for a given node based on a given attrs payload. | |
| 97 * @param {Array.<string>} attrs Attribute key-value pairs to set. | |
| 98 * @private | |
| 99 */ | |
| 100 devtools.DomNode.prototype.setAttributesPayload_ = function(attrs) { | |
| 101 for (var i = 0; i < attrs.length; i += 2) { | |
| 102 this.addAttribute_(attrs[i], attrs[i + 1]); | |
| 103 } | |
| 104 }; | |
| 105 | |
| 106 | |
| 107 /** | |
| 108 * @return True iff node has attributes. | |
| 109 */ | |
| 110 devtools.DomNode.prototype.hasAttributes = function() { | |
| 111 return this.attributes.length > 0; | |
| 112 }; | |
| 113 | |
| 114 | |
| 115 /** | |
| 116 * @return True iff node has child nodes. | |
| 117 */ | |
| 118 devtools.DomNode.prototype.hasChildNodes = function() { | |
| 119 return this.childNodesCount_ > 0; | |
| 120 }; | |
| 121 | |
| 122 | |
| 123 /** | |
| 124 * Inserts child node into this node after a given anchor. | |
| 125 * @param {devtools.DomNode} prev Node to insert child after. | |
| 126 * @param {Array.<Object>} payload Child node data. | |
| 127 * @private | |
| 128 */ | |
| 129 devtools.DomNode.prototype.insertChild_ = function(prev, payload) { | |
| 130 var node = new devtools.DomNode(this.ownerDocument, payload); | |
| 131 if (!prev) { | |
| 132 // First node | |
| 133 this.children = [ node ]; | |
| 134 } else { | |
| 135 this.children.splice(this.children.indexOf(prev) + 1, 0, node); | |
| 136 } | |
| 137 this.renumber_(); | |
| 138 return node; | |
| 139 }; | |
| 140 | |
| 141 | |
| 142 /** | |
| 143 * Removes child node from this node. | |
| 144 * @param {devtools.DomNode} node Node to remove. | |
| 145 * @private | |
| 146 */ | |
| 147 devtools.DomNode.prototype.removeChild_ = function(node) { | |
| 148 this.children.splice(this.children.indexOf(node), 1); | |
| 149 node.parentNode = null; | |
| 150 this.renumber_(); | |
| 151 }; | |
| 152 | |
| 153 | |
| 154 /** | |
| 155 * Sets children for this node based on the given payload. | |
| 156 * @param {Array.<Object>} payload Data for children. | |
| 157 * @private | |
| 158 */ | |
| 159 devtools.DomNode.prototype.setChildrenPayload_ = function(payloads) { | |
| 160 this.children = []; | |
| 161 for (var i = 0; i < payloads.length; ++i) { | |
| 162 var payload = payloads[i]; | |
| 163 var node = new devtools.DomNode(this.ownerDocument, payload); | |
| 164 this.children.push(node); | |
| 165 } | |
| 166 this.renumber_(); | |
| 167 }; | |
| 168 | |
| 169 | |
| 170 /** | |
| 171 * Normalizes prev/next/parent/firstChild links for this node's children. | |
| 172 * @private | |
| 173 */ | |
| 174 devtools.DomNode.prototype.renumber_ = function() { | |
| 175 this.childNodesCount_ = this.children.length; | |
| 176 if (this.childNodesCount_ == 0) { | |
| 177 this.firstChild = null; | |
| 178 return; | |
| 179 } | |
| 180 this.firstChild = this.children[0]; | |
| 181 for (var i = 0; i < this.childNodesCount_; ++i) { | |
| 182 var child = this.children[i]; | |
| 183 child.nextSibling = i + 1 < this.childNodesCount_ ? | |
| 184 this.children[i + 1] : null; | |
| 185 child.prevSibling = i - 1 >= 0 ? this.children[i - 1] : null; | |
| 186 child.parentNode = this; | |
| 187 } | |
| 188 }; | |
| 189 | |
| 190 | |
| 191 /** | |
| 192 * Returns attribute value. | |
| 193 * @param {string} name Attribute name to get value for. | |
| 194 * @return {string} Attribute value. | |
| 195 */ | |
| 196 devtools.DomNode.prototype.getAttribute = function(name) { | |
| 197 var attr = this.attributesMap_[name]; | |
| 198 return attr ? attr.value : undefined; | |
| 199 }; | |
| 200 | |
| 201 | |
| 202 /** | |
| 203 * Sends 'set attribute' command to the remote agent. | |
| 204 * @param {string} name Attribute name to set value for. | |
| 205 * @param {string} value Attribute value to set. | |
| 206 */ | |
| 207 devtools.DomNode.prototype.setAttribute = function(name, value) { | |
| 208 var self = this; | |
| 209 this.ownerDocument.domAgent_.setAttributeAsync(this, name, value, | |
| 210 function() { | |
| 211 var attr = self.attributesMap_[name]; | |
| 212 if (attr) { | |
| 213 attr.value = value; | |
| 214 } else { | |
| 215 attr = self.addAttribute_(name, value); | |
| 216 } | |
| 217 }); | |
| 218 }; | |
| 219 | |
| 220 | |
| 221 /** | |
| 222 * Creates an attribute-like object and adds it to the object. | |
| 223 * @param {string} name Attribute name to set value for. | |
| 224 * @param {string} value Attribute value to set. | |
| 225 */ | |
| 226 devtools.DomNode.prototype.addAttribute_ = function(name, value) { | |
| 227 var attr = { | |
| 228 'name': name, | |
| 229 'value': value, | |
| 230 node_: this | |
| 231 }; | |
| 232 | |
| 233 this.attributesMap_[name] = attr; | |
| 234 this.attributes.push(attr); | |
| 235 }; | |
| 236 | |
| 237 | |
| 238 /** | |
| 239 * Sends 'remove attribute' command to the remote agent. | |
| 240 * @param {string} name Attribute name to set value for. | |
| 241 */ | |
| 242 devtools.DomNode.prototype.removeAttribute = function(name) { | |
| 243 var self = this; | |
| 244 this.ownerDocument.domAgent_.removeAttributeAsync(this, name, function() { | |
| 245 delete self.attributesMap_[name]; | |
| 246 for (var i = 0; i < self.attributes.length; ++i) { | |
| 247 if (self.attributes[i].name == name) { | |
| 248 self.attributes.splice(i, 1); | |
| 249 break; | |
| 250 } | |
| 251 } | |
| 252 }); | |
| 253 }; | |
| 254 | |
| 255 | |
| 256 /** | |
| 257 * Makes available the following methods and properties: | |
| 258 * - node.style property | |
| 259 * - node.document.defaultView.getComputedStyles(node) | |
| 260 * - node.document.defaultView.getMatchedCSSRules(node, ...) | |
| 261 * - style attribute of node's attributes | |
| 262 * @param {string} computedStyle is a cssText of result of getComputedStyle(). | |
| 263 * @param {string} inlineStyle is a style.cssText (defined in the STYLE | |
| 264 * attribute). | |
| 265 * @param {Object} styleAttributes represents 'style' property | |
| 266 * of attributes. | |
| 267 * @param {Array.<object>} matchedCSSRules represents result of the | |
| 268 * getMatchedCSSRules(node, '', authorOnly). Each elemet consists of: | |
| 269 * selector, rule.style.cssText[, rule.parentStyleSheet.href | |
| 270 * [, rule.parentStyleSheet.ownerNode.nodeName]]. | |
| 271 */ | |
| 272 devtools.DomNode.prototype.setStyles = function(computedStyle, inlineStyle, | |
| 273 styleAttributes, matchedCSSRules) { | |
| 274 this.computedStyle_ = this.makeStyle_(computedStyle); | |
| 275 this.style = this.makeStyle_(inlineStyle); | |
| 276 | |
| 277 for (var name in styleAttributes) { | |
| 278 if (this.attributesMap_[name]) { | |
| 279 this.attributesMap_[name].style = | |
| 280 this.makeStyle_(styleAttributes[name]); | |
| 281 } | |
| 282 } | |
| 283 | |
| 284 this.matchedCSSRules_ = []; | |
| 285 for (var i = 0; i < matchedCSSRules.length; i++) { | |
| 286 var descr = matchedCSSRules[i]; | |
| 287 | |
| 288 var rule = {}; | |
| 289 rule.selectorText = descr['selector']; | |
| 290 rule.style = this.makeStyle_(descr['style']); | |
| 291 | |
| 292 if (descr['parentStyleSheet']) { | |
| 293 var parentStyleMock = {}; | |
| 294 parentStyleMock.href = descr['parentStyleSheet']['href']; | |
| 295 var nodeName = descr['parentStyleSheet']['ownerNodeName']; | |
| 296 if (nodeName) { | |
| 297 parentStyleMock.ownerNode = { | |
| 298 'nodeName': nodeName | |
| 299 }; | |
| 300 } | |
| 301 rule.parentStyleSheet = parentStyleMock; | |
| 302 } | |
| 303 this.matchedCSSRules_.push(rule); | |
| 304 } | |
| 305 }; | |
| 306 | |
| 307 | |
| 308 /** | |
| 309 * Creates a style declaration. | |
| 310 * @param {payload} payload | |
| 311 * @return {devtools.CSSStyleDeclaration:undefined} | |
| 312 * @see devtools.CSSStyleDeclaration | |
| 313 */ | |
| 314 devtools.DomNode.prototype.makeStyle_ = function(payload) { | |
| 315 var style = new devtools.CSSStyleDeclaration(payload); | |
| 316 style.nodeId_ = this.id_; | |
| 317 return style; | |
| 318 }; | |
| 319 | |
| 320 | |
| 321 /** | |
| 322 * Remove references to the style information to release | |
| 323 * resources when styles are not going to be used. | |
| 324 * @see setStyles. | |
| 325 */ | |
| 326 devtools.DomNode.prototype.clearStyles = function() { | |
| 327 this.computedStyle = null; | |
| 328 this.style = null; | |
| 329 for (var name in this.attributesMap_) { | |
| 330 this.attributesMap_[name].style = null; | |
| 331 } | |
| 332 this.matchedCSSRules_ = null; | |
| 333 }; | |
| 334 | |
| 335 | |
| 336 /** | |
| 337 * Remote Dom document abstraction. | |
| 338 * @param {devtools.DomAgent} domAgent owner agent. | |
| 339 * @param {devtools.DomWindow} defaultView owner window. | |
| 340 * @constructor. | |
| 341 */ | |
| 342 devtools.DomDocument = function(domAgent, defaultView) { | |
| 343 devtools.DomNode.call(this, null, | |
| 344 [ | |
| 345 0, // id | |
| 346 9, // type = Node.DOCUMENT_NODE, | |
| 347 '', // nodeName | |
| 348 '', // nodeValue | |
| 349 [], // attributes | |
| 350 0, // childNodeCount | |
| 351 ]); | |
| 352 this.listeners_ = {}; | |
| 353 this.domAgent_ = domAgent; | |
| 354 this.defaultView = defaultView; | |
| 355 }; | |
| 356 goog.inherits(devtools.DomDocument, devtools.DomNode); | |
| 357 | |
| 358 | |
| 359 /** | |
| 360 * Adds event listener to the Dom. | |
| 361 * @param {string} name Event name. | |
| 362 * @param {function(Event):undefined} callback Listener callback. | |
| 363 * @param {bool} useCapture Listener's useCapture settings. | |
| 364 */ | |
| 365 devtools.DomDocument.prototype.addEventListener = | |
| 366 function(name, callback, useCapture) { | |
| 367 var listeners = this.listeners_[name]; | |
| 368 if (!listeners) { | |
| 369 listeners = []; | |
| 370 this.listeners_[name] = listeners; | |
| 371 } | |
| 372 listeners.push(callback); | |
| 373 }; | |
| 374 | |
| 375 | |
| 376 /** | |
| 377 * Removes event listener from the Dom. | |
| 378 * @param {string} name Event name. | |
| 379 * @param {function(Event):undefined} callback Listener callback. | |
| 380 * @param {bool} useCapture Listener's useCapture settings. | |
| 381 */ | |
| 382 devtools.DomDocument.prototype.removeEventListener = | |
| 383 function(name, callback, useCapture) { | |
| 384 var listeners = this.listeners_[name]; | |
| 385 if (!listeners) { | |
| 386 return; | |
| 387 } | |
| 388 var index = listeners.indexOf(callback); | |
| 389 if (index != -1) { | |
| 390 listeners.splice(index, 1); | |
| 391 } | |
| 392 }; | |
| 393 | |
| 394 | |
| 395 /** | |
| 396 * Fires Dom event to the listeners for given event type. | |
| 397 * @param {string} name Event type. | |
| 398 * @param {Event} event Event to fire. | |
| 399 * @private | |
| 400 */ | |
| 401 devtools.DomDocument.prototype.fireDomEvent_ = function(name, event) { | |
| 402 var listeners = this.listeners_[name]; | |
| 403 if (!listeners) { | |
| 404 return; | |
| 405 } | |
| 406 for (var i = 0; i < listeners.length; ++i) { | |
| 407 listeners[i](event); | |
| 408 } | |
| 409 }; | |
| 410 | |
| 411 | |
| 412 | |
| 413 /** | |
| 414 * Simulation of inspected DOMWindow. | |
| 415 * @param {devtools.DomAgent} domAgent owner agent. | |
| 416 * @constructor | |
| 417 */ | |
| 418 devtools.DomWindow = function(domAgent) { | |
| 419 this.document = new devtools.DomDocument(domAgent, this); | |
| 420 }; | |
| 421 | |
| 422 /** | |
| 423 * Represents DOM Node class. | |
| 424 */ | |
| 425 devtools.DomWindow.prototype.__defineGetter__('Node', function() { | |
| 426 return devtools.DomNode; | |
| 427 }); | |
| 428 | |
| 429 /** | |
| 430 * Represents DOM Element class. | |
| 431 * @constructor | |
| 432 */ | |
| 433 devtools.DomWindow.prototype.__defineGetter__('Element', function() { | |
| 434 return devtools.DomNode; | |
| 435 }); | |
| 436 | |
| 437 | |
| 438 /** | |
| 439 * See usages in ScopeChainSidebarPane.js where it's called as | |
| 440 * constructor. | |
| 441 */ | |
| 442 devtools.DomWindow.prototype.Object = function() { | |
| 443 }; | |
| 444 | |
| 445 | |
| 446 /** | |
| 447 * Simulates the DOM interface for styles. | |
| 448 * @param {devtools.DomNode} node | |
| 449 * @return {CSSStyleDescription} | |
| 450 */ | |
| 451 devtools.DomWindow.prototype.getComputedStyle = function(node) { | |
| 452 return node.computedStyle_; | |
| 453 }; | |
| 454 | |
| 455 | |
| 456 /** | |
| 457 * Simulates the DOM interface for styles. | |
| 458 * @param {devtools.DomNode} nodeStyles | |
| 459 * @param {string} pseudoElement assumed to be empty string. | |
| 460 * @param {boolean} authorOnly assumed to be equal to authorOnly argument of | |
| 461 * getNodeStylesAsync. | |
| 462 * @return {CSSStyleDescription} | |
| 463 */ | |
| 464 devtools.DomWindow.prototype.getMatchedCSSRules = function(node, | |
| 465 pseudoElement, authorOnly) { | |
| 466 return node.matchedCSSRules_; | |
| 467 }; | |
| 468 | |
| 469 | |
| 470 /** | |
| 471 * Creates DomAgent Js representation. | |
| 472 * @constructor | |
| 473 */ | |
| 474 devtools.DomAgent = function() { | |
| 475 RemoteDomAgent.DidGetChildNodes = | |
| 476 devtools.Callback.processCallback; | |
| 477 RemoteDomAgent.DidPerformSearch = | |
| 478 devtools.Callback.processCallback; | |
| 479 RemoteDomAgent.DidApplyDomChange = | |
| 480 devtools.Callback.processCallback; | |
| 481 RemoteDomAgent.DidRemoveAttribute = | |
| 482 devtools.Callback.processCallback; | |
| 483 RemoteDomAgent.DidSetTextNodeValue = | |
| 484 devtools.Callback.processCallback; | |
| 485 RemoteDomAgent.AttributesUpdated = | |
| 486 goog.bind(this.attributesUpdated, this); | |
| 487 RemoteDomAgent.SetDocumentElement = | |
| 488 goog.bind(this.setDocumentElement, this); | |
| 489 RemoteDomAgent.SetChildNodes = | |
| 490 goog.bind(this.setChildNodes, this); | |
| 491 RemoteDomAgent.HasChildrenUpdated = | |
| 492 goog.bind(this.hasChildrenUpdated, this); | |
| 493 RemoteDomAgent.ChildNodeInserted = | |
| 494 goog.bind(this.childNodeInserted, this); | |
| 495 RemoteDomAgent.ChildNodeRemoved = | |
| 496 goog.bind(this.childNodeRemoved, this); | |
| 497 | |
| 498 /** | |
| 499 * Top-level (and the only) document. | |
| 500 * @type {devtools.DomWindow} | |
| 501 * @private | |
| 502 */ | |
| 503 this.window_ = null; | |
| 504 | |
| 505 /** | |
| 506 * Id to node mapping. | |
| 507 * @type {Object} | |
| 508 * @private | |
| 509 */ | |
| 510 this.idToDomNode_ = null; | |
| 511 | |
| 512 /** | |
| 513 * @type {Array.<number>} Node ids for search results. | |
| 514 * @private | |
| 515 */ | |
| 516 this.searchResults_ = null; | |
| 517 }; | |
| 518 | |
| 519 | |
| 520 /** | |
| 521 * Resets dom agent to its initial state. | |
| 522 */ | |
| 523 devtools.DomAgent.prototype.reset = function() { | |
| 524 this.window_ = new devtools.DomWindow(this); | |
| 525 this.idToDomNode_ = { 0 : this.getDocument() }; | |
| 526 this.searchResults_ = []; | |
| 527 }; | |
| 528 | |
| 529 | |
| 530 /** | |
| 531 * @return {devtools.DomWindow} Window for the top level (and the only) document
. | |
| 532 */ | |
| 533 devtools.DomAgent.prototype.getWindow = function() { | |
| 534 return this.window_; | |
| 535 }; | |
| 536 | |
| 537 | |
| 538 /** | |
| 539 * @return {devtools.DomDocument} A document of the top level window. | |
| 540 */ | |
| 541 devtools.DomAgent.prototype.getDocument = function() { | |
| 542 return this.window_.document; | |
| 543 }; | |
| 544 | |
| 545 | |
| 546 /** | |
| 547 * Requests that the document element is sent from the agent. | |
| 548 */ | |
| 549 devtools.DomAgent.prototype.getDocumentElementAsync = function() { | |
| 550 if (this.getDocument().documentElement) { | |
| 551 return; | |
| 552 } | |
| 553 RemoteDomAgent.GetDocumentElement(); | |
| 554 }; | |
| 555 | |
| 556 | |
| 557 /** | |
| 558 * Asynchronously fetches children from the element with given id. | |
| 559 * @param {devtools.DomNode} parent Element to get children for. | |
| 560 * @param {function(devtools.DomNode):undefined} opt_callback Callback with | |
| 561 * the result. | |
| 562 */ | |
| 563 devtools.DomAgent.prototype.getChildNodesAsync = function(parent, | |
| 564 opt_callback) { | |
| 565 var children = parent.children; | |
| 566 if (children && opt_callback) { | |
| 567 opt_callback(children); | |
| 568 return; | |
| 569 } | |
| 570 var mycallback = function() { | |
| 571 if (opt_callback) { | |
| 572 opt_callback(parent.children); | |
| 573 } | |
| 574 }; | |
| 575 var callId = devtools.Callback.wrap(mycallback); | |
| 576 RemoteDomAgent.GetChildNodes(callId, parent.id_); | |
| 577 }; | |
| 578 | |
| 579 | |
| 580 /** | |
| 581 * Sends 'set attribute' command to the remote agent. | |
| 582 * @param {devtools.DomNode} node Node to change. | |
| 583 * @param {string} name Attribute name to set value for. | |
| 584 * @param {string} value Attribute value to set. | |
| 585 * @param {function():undefined} opt_callback Callback on success. | |
| 586 */ | |
| 587 devtools.DomAgent.prototype.setAttributeAsync = function(node, name, value, | |
| 588 callback) { | |
| 589 var mycallback = goog.bind(this.didApplyDomChange_, this, node, callback); | |
| 590 RemoteDomAgent.SetAttribute(devtools.Callback.wrap(mycallback), | |
| 591 node.id_, name, value); | |
| 592 }; | |
| 593 | |
| 594 | |
| 595 /** | |
| 596 * Sends 'remove attribute' command to the remote agent. | |
| 597 * @param {devtools.DomNode} node Node to change. | |
| 598 * @param {string} name Attribute name to set value for. | |
| 599 * @param {function():undefined} opt_callback Callback on success. | |
| 600 */ | |
| 601 devtools.DomAgent.prototype.removeAttributeAsync = function(node, name, | |
| 602 callback) { | |
| 603 var mycallback = goog.bind(this.didApplyDomChange_, this, node, callback); | |
| 604 RemoteDomAgent.RemoveAttribute(devtools.Callback.wrap(mycallback), | |
| 605 node.id_, name); | |
| 606 }; | |
| 607 | |
| 608 | |
| 609 /** | |
| 610 * Sends 'set text node value' command to the remote agent. | |
| 611 * @param {devtools.DomNode} node Node to change. | |
| 612 * @param {string} text Text value to set. | |
| 613 * @param {function():undefined} opt_callback Callback on success. | |
| 614 */ | |
| 615 devtools.DomAgent.prototype.setTextNodeValueAsync = function(node, text, | |
| 616 callback) { | |
| 617 var mycallback = goog.bind(this.didApplyDomChange_, this, node, callback); | |
| 618 RemoteDomAgent.SetTextNodeValue(devtools.Callback.wrap(mycallback), | |
| 619 node.id_, text); | |
| 620 }; | |
| 621 | |
| 622 | |
| 623 /** | |
| 624 * Universal callback wrapper for edit dom operations. | |
| 625 * @param {devtools.DomNode} node Node to apply local changes on. | |
| 626 * @param {Function} callback Post-operation call. | |
| 627 * @param {boolean} success True iff operation has completed successfully. | |
| 628 */ | |
| 629 devtools.DomAgent.prototype.didApplyDomChange_ = function(node, | |
| 630 callback, success) { | |
| 631 if (!success) { | |
| 632 return; | |
| 633 } | |
| 634 callback(); | |
| 635 var elem = WebInspector.panels.elements.treeOutline.findTreeElement(node); | |
| 636 if (elem) { | |
| 637 elem._updateTitle(); | |
| 638 } | |
| 639 }; | |
| 640 | |
| 641 | |
| 642 /** | |
| 643 * @see DomAgentDelegate. | |
| 644 * {@inheritDoc}. | |
| 645 */ | |
| 646 devtools.DomAgent.prototype.attributesUpdated = function(nodeId, attrsArray) { | |
| 647 var node = this.idToDomNode_[nodeId]; | |
| 648 node.setAttributesPayload_(attrsArray); | |
| 649 }; | |
| 650 | |
| 651 | |
| 652 /** | |
| 653 * Returns node for id. | |
| 654 * @param {number} nodeId Id to get node for. | |
| 655 * @return {devtools.DomNode} Node with given id. | |
| 656 */ | |
| 657 devtools.DomAgent.prototype.getNodeForId = function(nodeId) { | |
| 658 return this.idToDomNode_[nodeId]; | |
| 659 }; | |
| 660 | |
| 661 | |
| 662 /** | |
| 663 * @see DomAgentDelegate. | |
| 664 * {@inheritDoc}. | |
| 665 */ | |
| 666 devtools.DomAgent.prototype.setDocumentElement = function(payload) { | |
| 667 var doc = this.getDocument(); | |
| 668 if (doc.documentElement) { | |
| 669 this.reset(); | |
| 670 doc = this.getDocument(); | |
| 671 } | |
| 672 this.setChildNodes(0, [payload]); | |
| 673 doc.documentElement = doc.firstChild; | |
| 674 doc.documentElement.ownerDocument = doc; | |
| 675 WebInspector.panels.elements.reset(); | |
| 676 }; | |
| 677 | |
| 678 | |
| 679 /** | |
| 680 * @see DomAgentDelegate. | |
| 681 * {@inheritDoc}. | |
| 682 */ | |
| 683 devtools.DomAgent.prototype.setChildNodes = function(parentId, payloads) { | |
| 684 var parent = this.idToDomNode_[parentId]; | |
| 685 if (parent.children) { | |
| 686 return; | |
| 687 } | |
| 688 parent.setChildrenPayload_(payloads); | |
| 689 this.bindNodes_(parent.children); | |
| 690 }; | |
| 691 | |
| 692 | |
| 693 /** | |
| 694 * Binds nodes to ids recursively. | |
| 695 * @param {Array.<devtools.DomNode>} children Nodes to bind. | |
| 696 */ | |
| 697 devtools.DomAgent.prototype.bindNodes_ = function(children) { | |
| 698 for (var i = 0; i < children.length; ++i) { | |
| 699 var child = children[i]; | |
| 700 this.idToDomNode_[child.id_] = child; | |
| 701 if (child.children) { | |
| 702 this.bindNodes_(child.children); | |
| 703 } | |
| 704 } | |
| 705 }; | |
| 706 | |
| 707 | |
| 708 /** | |
| 709 * @see DomAgentDelegate. | |
| 710 * {@inheritDoc}. | |
| 711 */ | |
| 712 devtools.DomAgent.prototype.hasChildrenUpdated = function(nodeId, newValue) { | |
| 713 var node = this.idToDomNode_[nodeId]; | |
| 714 var outline = WebInspector.panels.elements.treeOutline; | |
| 715 var treeElement = outline.findTreeElement(node); | |
| 716 if (treeElement) { | |
| 717 treeElement.hasChildren = newValue; | |
| 718 treeElement.whitespaceIgnored = Preferences.ignoreWhitespace; | |
| 719 } | |
| 720 }; | |
| 721 | |
| 722 | |
| 723 /** | |
| 724 * @see DomAgentDelegate. | |
| 725 * {@inheritDoc}. | |
| 726 */ | |
| 727 devtools.DomAgent.prototype.childNodeInserted = function( | |
| 728 parentId, prevId, payload) { | |
| 729 var parent = this.idToDomNode_[parentId]; | |
| 730 var prev = this.idToDomNode_[prevId]; | |
| 731 var node = parent.insertChild_(prev, payload); | |
| 732 this.idToDomNode_[node.id_] = node; | |
| 733 var event = { target : node, relatedNode : parent }; | |
| 734 this.getDocument().fireDomEvent_('DOMNodeInserted', event); | |
| 735 }; | |
| 736 | |
| 737 | |
| 738 /** | |
| 739 * @see DomAgentDelegate. | |
| 740 * {@inheritDoc}. | |
| 741 */ | |
| 742 devtools.DomAgent.prototype.childNodeRemoved = function( | |
| 743 parentId, nodeId) { | |
| 744 var parent = this.idToDomNode_[parentId]; | |
| 745 var node = this.idToDomNode_[nodeId]; | |
| 746 parent.removeChild_(node); | |
| 747 var event = { target : node, relatedNode : parent }; | |
| 748 this.getDocument().fireDomEvent_('DOMNodeRemoved', event); | |
| 749 delete this.idToDomNode_[nodeId]; | |
| 750 }; | |
| 751 | |
| 752 | |
| 753 /** | |
| 754 * @see DomAgentDelegate. | |
| 755 * {@inheritDoc}. | |
| 756 */ | |
| 757 devtools.DomAgent.prototype.performSearch = function(query, callback) { | |
| 758 this.searchResults_ = []; | |
| 759 RemoteDomAgent.PerformSearch( | |
| 760 devtools.Callback.wrap( | |
| 761 goog.bind(this.performSearchCallback_, this, callback, | |
| 762 this.searchResults_)), | |
| 763 query); | |
| 764 }; | |
| 765 | |
| 766 | |
| 767 /** | |
| 768 * Invokes callback for nodes that needs to clear highlighting. | |
| 769 * @param {function(Array.<devtools.DomNode>)} callback to accept the result. | |
| 770 */ | |
| 771 devtools.DomAgent.prototype.searchCanceled = function(callback) { | |
| 772 if (!this.searchResults_) | |
| 773 return; | |
| 774 | |
| 775 var nodes = []; | |
| 776 for (var i = 0; i < this.searchResults_.length; ++i) { | |
| 777 var nodeId = this.searchResults_[i]; | |
| 778 var node = this.idToDomNode_[nodeId]; | |
| 779 nodes.push(node); | |
| 780 } | |
| 781 | |
| 782 callback(nodes); | |
| 783 this.searchResults_ = null; | |
| 784 }; | |
| 785 | |
| 786 | |
| 787 /** | |
| 788 * Invokes callback for each node that needs to gain highlighting. | |
| 789 * @param {function(Array.<devtools.DomNode>)} callback to accept the result. | |
| 790 * @param {Array.<number>} searchResults to be populated. | |
| 791 * @param {Array.<number>} nodeIds Ids to highlight. | |
| 792 */ | |
| 793 devtools.DomAgent.prototype.performSearchCallback_ = function(callback, | |
| 794 searchResults, nodeIds) { | |
| 795 | |
| 796 if (this.searchResults_ !== searchResults) | |
| 797 return; // another search has requested and this results are obsolete | |
| 798 | |
| 799 var nodes = []; | |
| 800 | |
| 801 for (var i = 0; i < nodeIds.length; ++i) { | |
| 802 var node = this.idToDomNode_[nodeIds[i]]; | |
| 803 searchResults.push(nodeIds[i]); | |
| 804 nodes.push(node); | |
| 805 } | |
| 806 | |
| 807 callback(nodes); | |
| 808 }; | |
| 809 | |
| 810 | |
| 811 /** | |
| 812 * Returns a node by index from the actual search results | |
| 813 * (last performSearch). | |
| 814 * @param {number} index in the results. | |
| 815 * @return {devtools.DomNode} | |
| 816 */ | |
| 817 devtools.DomAgent.prototype.getSearchResultNode = function(index) { | |
| 818 return this.idToDomNode_[this.searchResults_[index]]; | |
| 819 }; | |
| 820 | |
| 821 | |
| 822 /** | |
| 823 * Returns all properties of the given node. | |
| 824 * @param {number} nodeId Node to get properties for. | |
| 825 * @param {Array.<string>} path Path to the object. | |
| 826 * @param {number} protoDepth Depth to the exact proto level. | |
| 827 * @param {function(string):undefined} callback Function to call with the | |
| 828 * result. | |
| 829 */ | |
| 830 devtools.DomAgent.prototype.getNodePropertiesAsync = function(nodeId, | |
| 831 path, protoDepth, callback) { | |
| 832 var callbackId = this.utilityFunctionCallbackWrapper_(callback); | |
| 833 RemoteToolsAgent.ExecuteUtilityFunction(callbackId, | |
| 834 'getProperties', nodeId, | |
| 835 JSON.stringify([path, protoDepth])); | |
| 836 }; | |
| 837 | |
| 838 | |
| 839 /** | |
| 840 * Returns prototype chain for a given node. | |
| 841 * @param {number} nodeId Node to get prototypes for. | |
| 842 * @param {Function} callback. | |
| 843 */ | |
| 844 devtools.DomAgent.prototype.getNodePrototypesAsync = function(nodeId, | |
| 845 callback) { | |
| 846 var callbackId = this.utilityFunctionCallbackWrapper_(callback); | |
| 847 RemoteToolsAgent.ExecuteUtilityFunction(callbackId, | |
| 848 'getPrototypes', nodeId, '[]'); | |
| 849 }; | |
| 850 | |
| 851 | |
| 852 /** | |
| 853 * Returns styles for given node. | |
| 854 * @param {devtools.DomNode} node Node to get prototypes for. | |
| 855 * @param {boolean} authorOnly Returns only author styles if true. | |
| 856 * @param {Function} callback. | |
| 857 */ | |
| 858 devtools.DomAgent.prototype.getNodeStylesAsync = function(node, | |
| 859 authorOnly, callback) { | |
| 860 var callbackId = this.utilityFunctionCallbackWrapper_(callback); | |
| 861 RemoteToolsAgent.ExecuteUtilityFunction(callbackId, | |
| 862 'getStyles', | |
| 863 node.id_, | |
| 864 JSON.stringify([authorOnly])); | |
| 865 }; | |
| 866 | |
| 867 | |
| 868 /** | |
| 869 * Toggles style with given id on/off. | |
| 870 * @param {devtools.CSSStyleDeclaration} style Style to toggle. | |
| 871 * @param {boolean} enabled True if style should be enabled. | |
| 872 * @param {string} name Style name. | |
| 873 * @param {Function} callback. | |
| 874 */ | |
| 875 devtools.DomAgent.prototype.toggleNodeStyleAsync = function( | |
| 876 style, enabled, name, callback) { | |
| 877 var callbackId = this.utilityFunctionCallbackWrapper_(callback); | |
| 878 RemoteToolsAgent.ExecuteUtilityFunction(callbackId, | |
| 879 'toggleNodeStyle', | |
| 880 style.nodeId_, | |
| 881 JSON.stringify([style.id_, enabled, name])); | |
| 882 }; | |
| 883 | |
| 884 | |
| 885 /** | |
| 886 * Applies new text to a style. | |
| 887 * @param {devtools.CSSStyleDeclaration} style Style to edit. | |
| 888 * @param {string} name Property name to edit. | |
| 889 * @param {string} styleText Text to set the style from. | |
| 890 * @param {Function} callback. | |
| 891 */ | |
| 892 devtools.DomAgent.prototype.applyStyleTextAsync = function( | |
| 893 style, name, styleText, callback) { | |
| 894 var callbackId = this.utilityFunctionCallbackWrapper_(callback); | |
| 895 RemoteToolsAgent.ExecuteUtilityFunction( | |
| 896 callbackId, | |
| 897 'applyStyleText', | |
| 898 style.nodeId_, | |
| 899 JSON.stringify([style.id_, name, styleText])); | |
| 900 }; | |
| 901 | |
| 902 | |
| 903 /** | |
| 904 * Sets style property with given name to a value. | |
| 905 * @param {devtools.DomNode} node Node to edit style for. | |
| 906 * @param {string} name Property name to edit. | |
| 907 * @param {string} value New value. | |
| 908 * @param {Function} callback. | |
| 909 */ | |
| 910 devtools.DomAgent.prototype.setStylePropertyAsync = function( | |
| 911 node, name, value, callback) { | |
| 912 var callbackId = this.utilityFunctionCallbackWrapper_(callback); | |
| 913 RemoteToolsAgent.ExecuteUtilityFunction( | |
| 914 callbackId, | |
| 915 'setStyleProperty', | |
| 916 node.id_, | |
| 917 JSON.stringify([name, value])); | |
| 918 }; | |
| 919 | |
| 920 | |
| 921 /** | |
| 922 * Dumps exception if something went wrong in ExecuteUtilityFunction. | |
| 923 * @param {Function} callback Callback to wrap. | |
| 924 * @return {number} Callback id. | |
| 925 */ | |
| 926 devtools.DomAgent.prototype.utilityFunctionCallbackWrapper_ = | |
| 927 function(callback) { | |
| 928 var mycallback = function(result, exception) { | |
| 929 if (exception && exception.length) { | |
| 930 debugPrint('Exception in ExecuteUtilityFunction styles:' + exception); | |
| 931 return; | |
| 932 } | |
| 933 callback(result); | |
| 934 }; | |
| 935 return devtools.Callback.wrap(mycallback); | |
| 936 }; | |
| 937 | |
| 938 | |
| 939 /** | |
| 940 * Represents remote CSSStyleDeclaration for using in StyleSidebarPane. | |
| 941 * @param {id, Array<Object>} payload built by inject's getStyle from the | |
| 942 * injected js. | |
| 943 * @constructor | |
| 944 */ | |
| 945 devtools.CSSStyleDeclaration = function(payload) { | |
| 946 this.id_ = payload[0]; | |
| 947 this.width = payload[1]; | |
| 948 this.height = payload[2]; | |
| 949 this.__disabledProperties = payload[3]; | |
| 950 this.__disabledPropertyValues = payload[4]; | |
| 951 this.__disabledPropertyPriorities = payload[5]; | |
| 952 | |
| 953 this.length = payload.length - 6; | |
| 954 this.priority_ = {}; | |
| 955 this.implicit_ = {}; | |
| 956 this.shorthand_ = {}; | |
| 957 this.value_ = {}; | |
| 958 | |
| 959 for (var i = 6; i < payload.length; ++i) { | |
| 960 var p = payload[i]; | |
| 961 var name = p[0]; | |
| 962 | |
| 963 this.priority_[name] = p[1]; | |
| 964 this.implicit_[name] = p[2]; | |
| 965 this.shorthand_[name] = p[3]; | |
| 966 this.value_[name] = p[4]; | |
| 967 | |
| 968 this[i - 6] = name; | |
| 969 } | |
| 970 }; | |
| 971 | |
| 972 | |
| 973 /** | |
| 974 * @param {string} name of a CSS property. | |
| 975 * @return {string} | |
| 976 */ | |
| 977 devtools.CSSStyleDeclaration.prototype.getPropertyValue = function(name) { | |
| 978 return this.value_[name] || ''; | |
| 979 }; | |
| 980 | |
| 981 | |
| 982 /** | |
| 983 * @param {string} name of a CSS property. | |
| 984 * @return {string} 'important' | ''. | |
| 985 */ | |
| 986 devtools.CSSStyleDeclaration.prototype.getPropertyPriority = function(name) { | |
| 987 return this.priority_[name] || ''; | |
| 988 }; | |
| 989 | |
| 990 | |
| 991 /** | |
| 992 * @param {string} name of a CSS property. | |
| 993 * @return {string} shorthand name or '' | |
| 994 */ | |
| 995 devtools.CSSStyleDeclaration.prototype.getPropertyShorthand = function(name) { | |
| 996 return this.shorthand_[name] || ''; | |
| 997 }; | |
| 998 | |
| 999 | |
| 1000 /** | |
| 1001 * @param {string} name of a CSS property. | |
| 1002 * @return {boolean} | |
| 1003 */ | |
| 1004 devtools.CSSStyleDeclaration.prototype.isPropertyImplicit = function(name) { | |
| 1005 return !!this.implicit_[name]; | |
| 1006 }; | |
| 1007 | |
| 1008 | |
| 1009 function firstChildSkippingWhitespace() { | |
| 1010 return this.firstChild; | |
| 1011 } | |
| 1012 | |
| 1013 | |
| 1014 function onlyTextChild() { | |
| 1015 if (!this.children) { | |
| 1016 return null; | |
| 1017 } else if (this.children.length == 1 && | |
| 1018 this.children[0].nodeType == Node.TEXT_NODE) { | |
| 1019 return this.children[0]; | |
| 1020 } else { | |
| 1021 return null; | |
| 1022 } | |
| 1023 } | |
| OLD | NEW |