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