1 /* 2 * Copyright (C) 2009 Apple Inc. All rights reserved. 3 * Copyright (C) 2009 Google Inc. All rights reserved. 4 * Copyright (C) 2009 Joseph Pecoraro 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 16 * its contributors may be used to endorse or promote products derived 17 * from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "InspectorDOMAgent.h" 33 34 #if ENABLE(INSPECTOR) 35 36 #include "AtomicString.h" 37 #include "ContainerNode.h" 38 #include "Cookie.h" 39 #include "CookieJar.h" 40 #include "DOMWindow.h" 41 #include "Document.h" 42 #include "DocumentType.h" 43 #include "Event.h" 44 #include "EventListener.h" 45 #include "EventNames.h" 46 #include "EventTarget.h" 47 #include "HTMLFrameOwnerElement.h" 48 #include "InspectorFrontend.h" 49 #include "markup.h" 50 #include "MutationEvent.h" 51 #include "Node.h" 52 #include "NodeList.h" 53 #include "PlatformString.h" 54 #include "ScriptEventListener.h" 55 #include "ScriptObject.h" 56 #include "Text.h" 57 58 #include <wtf/OwnPtr.h> 59 #include <wtf/Vector.h> 60 61 namespace WebCore { 62 63 InspectorDOMAgent::InspectorDOMAgent(InspectorFrontend* frontend) 64 : EventListener(InspectorDOMAgentType) 65 , m_frontend(frontend) 66 , m_lastNodeId(1) 67 { 68 } 69 70 InspectorDOMAgent::~InspectorDOMAgent() 71 { 72 reset(); 73 } 74 75 void InspectorDOMAgent::reset() 76 { 77 discardBindings(); 78 79 ListHashSet<RefPtr<Document> > copy = m_documents; 80 for (ListHashSet<RefPtr<Document> >::iterator it = copy.begin(); it != copy.end(); ++it) 81 stopListening((*it).get()); 82 83 ASSERT(!m_documents.size()); 84 } 85 86 void InspectorDOMAgent::setDocument(Document* doc) 87 { 88 if (doc == mainFrameDocument()) 89 return; 90 91 reset(); 92 93 if (doc) { 94 startListening(doc); 95 if (doc->documentElement()) 96 pushDocumentToFrontend(); 97 } else 98 m_frontend->setDocument(ScriptObject()); 99 } 100 101 void InspectorDOMAgent::releaseDanglingNodes() 102 { 103 deleteAllValues(m_danglingNodeToIdMaps); 104 m_danglingNodeToIdMaps.clear(); 105 } 106 107 void InspectorDOMAgent::startListening(Document* doc) 108 { 109 if (m_documents.contains(doc)) 110 return; 111 112 doc->addEventListener(eventNames().DOMContentLoadedEvent, this, false); 113 doc->addEventListener(eventNames().loadEvent, this, true); 114 m_documents.add(doc); 115 } 116 117 void InspectorDOMAgent::stopListening(Document* doc) 118 { 119 if (!m_documents.contains(doc)) 120 return; 121 122 doc->removeEventListener(eventNames().DOMContentLoadedEvent, this, false); 123 doc->removeEventListener(eventNames().loadEvent, this, true); 124 m_documents.remove(doc); 125 } 126 127 void InspectorDOMAgent::handleEvent(ScriptExecutionContext*, Event* event) 128 { 129 AtomicString type = event->type(); 130 Node* node = event->target()->toNode(); 131 132 if (type == eventNames().DOMContentLoadedEvent) { 133 // Re-push document once it is loaded. 134 discardBindings(); 135 pushDocumentToFrontend(); 136 } else if (type == eventNames().loadEvent) { 137 long frameOwnerId = m_documentNodeToIdMap.get(node); 138 if (!frameOwnerId) 139 return; 140 141 if (!m_childrenRequested.contains(frameOwnerId)) { 142 // No children are mapped yet -> only notify on changes of hasChildren. 143 m_frontend->childNodeCountUpdated(frameOwnerId, innerChildNodeCount(node)); 144 } else { 145 // Re-add frame owner element together with its new children. 146 long parentId = m_documentNodeToIdMap.get(innerParentNode(node)); 147 m_frontend->childNodeRemoved(parentId, frameOwnerId); 148 ScriptObject value = buildObjectForNode(node, 0, &m_documentNodeToIdMap); 149 Node* previousSibling = innerPreviousSibling(node); 150 long prevId = previousSibling ? m_documentNodeToIdMap.get(previousSibling) : 0; 151 m_frontend->childNodeInserted(parentId, prevId, value); 152 // Invalidate children requested flag for the element. 153 m_childrenRequested.remove(m_childrenRequested.find(frameOwnerId)); 154 } 155 } 156 } 157 158 long InspectorDOMAgent::bind(Node* node, NodeToIdMap* nodesMap) 159 { 160 long id = nodesMap->get(node); 161 if (id) 162 return id; 163 id = m_lastNodeId++; 164 nodesMap->set(node, id); 165 m_idToNode.set(id, node); 166 m_idToNodesMap.set(id, nodesMap); 167 return id; 168 } 169 170 void InspectorDOMAgent::unbind(Node* node, NodeToIdMap* nodesMap) 171 { 172 if (node->isFrameOwnerElement()) { 173 const HTMLFrameOwnerElement* frameOwner = static_cast<const HTMLFrameOwnerElement*>(node); 174 stopListening(frameOwner->contentDocument()); 175 } 176 177 int id = nodesMap->get(node); 178 if (!id) 179 return; 180 m_idToNode.remove(id); 181 nodesMap->remove(node); 182 bool childrenRequested = m_childrenRequested.contains(id); 183 if (childrenRequested) { 184 // Unbind subtree known to client recursively. 185 m_childrenRequested.remove(id); 186 Node* child = innerFirstChild(node); 187 while (child) { 188 unbind(child, nodesMap); 189 child = innerNextSibling(child); 190 } 191 } 192 } 193 194 bool InspectorDOMAgent::pushDocumentToFrontend() 195 { 196 Document* document = mainFrameDocument(); 197 if (!document) 198 return false; 199 if (!m_documentNodeToIdMap.contains(document)) 200 m_frontend->setDocument(buildObjectForNode(document, 2, &m_documentNodeToIdMap)); 201 return true; 202 } 203 204 void InspectorDOMAgent::pushChildNodesToFrontend(long nodeId) 205 { 206 Node* node = nodeForId(nodeId); 207 if (!node || (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE && node->nodeType() != Node::DOCUMENT_FRAGMENT_NODE)) 208 return; 209 if (m_childrenRequested.contains(nodeId)) 210 return; 211 212 NodeToIdMap* nodeMap = m_idToNodesMap.get(nodeId); 213 ScriptArray children = buildArrayForContainerChildren(node, 1, nodeMap); 214 m_childrenRequested.add(nodeId); 215 m_frontend->setChildNodes(nodeId, children); 216 } 217 218 void InspectorDOMAgent::discardBindings() 219 { 220 m_documentNodeToIdMap.clear(); 221 m_idToNode.clear(); 222 releaseDanglingNodes(); 223 m_childrenRequested.clear(); 224 } 225 226 Node* InspectorDOMAgent::nodeForId(long id) 227 { 228 if (!id) 229 return 0; 230 231 HashMap<long, Node*>::iterator it = m_idToNode.find(id); 232 if (it != m_idToNode.end()) 233 return it->second; 234 return 0; 235 } 236 237 Node* InspectorDOMAgent::nodeForPath(const String& path) 238 { 239 // The path is of form "1,HTML,2,BODY,1,DIV" 240 Node* node = mainFrameDocument(); 241 if (!node) 242 return 0; 243 244 Vector<String> pathTokens; 245 path.split(",", false, pathTokens); 246 for (size_t i = 0; i < pathTokens.size() - 1; i += 2) { 247 bool success = true; 248 unsigned childNumber = pathTokens[i].toUInt(&success); 249 if (!success) 250 return 0; 251 if (childNumber >= innerChildNodeCount(node)) 252 return 0; 253 254 Node* child = innerFirstChild(node); 255 String childName = pathTokens[i + 1]; 256 for (size_t j = 0; child && j < childNumber; ++j) 257 child = innerNextSibling(child); 258 259 if (!child || child->nodeName() != childName) 260 return 0; 261 node = child; 262 } 263 return node; 264 } 265 266 void InspectorDOMAgent::getChildNodes(long callId, long nodeId) 267 { 268 pushChildNodesToFrontend(nodeId); 269 m_frontend->didGetChildNodes(callId); 270 } 271 272 long InspectorDOMAgent::pushNodePathToFrontend(Node* nodeToPush) 273 { 274 ASSERT(nodeToPush); // Invalid input 275 276 // If we are sending information to the client that is currently being created. Send root node first. 277 if (!pushDocumentToFrontend()) 278 return 0; 279 280 // Return id in case the node is known. 281 long result = m_documentNodeToIdMap.get(nodeToPush); 282 if (result) 283 return result; 284 285 Node* node = nodeToPush; 286 Vector<Node*> path; 287 NodeToIdMap* danglingMap = 0; 288 while (true) { 289 Node* parent = innerParentNode(node); 290 if (!parent) { 291 // Node being pushed is detached -> push subtree root. 292 danglingMap = new NodeToIdMap(); 293 m_danglingNodeToIdMaps.append(danglingMap); 294 m_frontend->setDetachedRoot(buildObjectForNode(node, 0, danglingMap)); 295 break; 296 } else { 297 path.append(parent); 298 if (m_documentNodeToIdMap.get(parent)) 299 break; 300 else 301 node = parent; 302 } 303 } 304 305 NodeToIdMap* map = danglingMap ? danglingMap : &m_documentNodeToIdMap; 306 for (int i = path.size() - 1; i >= 0; --i) { 307 long nodeId = map->get(path.at(i)); 308 ASSERT(nodeId); 309 pushChildNodesToFrontend(nodeId); 310 } 311 return map->get(nodeToPush); 312 } 313 314 void InspectorDOMAgent::setAttribute(long callId, long elementId, const String& name, const String& value) 315 { 316 Node* node = nodeForId(elementId); 317 if (node && (node->nodeType() == Node::ELEMENT_NODE)) { 318 Element* element = static_cast<Element*>(node); 319 ExceptionCode ec = 0; 320 element->setAttribute(name, value, ec); 321 m_frontend->didApplyDomChange(callId, ec == 0); 322 } else { 323 m_frontend->didApplyDomChange(callId, false); 324 } 325 } 326 327 void InspectorDOMAgent::removeAttribute(long callId, long elementId, const String& name) 328 { 329 Node* node = nodeForId(elementId); 330 if (node && (node->nodeType() == Node::ELEMENT_NODE)) { 331 Element* element = static_cast<Element*>(node); 332 ExceptionCode ec = 0; 333 element->removeAttribute(name, ec); 334 m_frontend->didApplyDomChange(callId, ec == 0); 335 } else { 336 m_frontend->didApplyDomChange(callId, false); 337 } 338 } 339 340 void InspectorDOMAgent::setTextNodeValue(long callId, long nodeId, const String& value) 341 { 342 Node* node = nodeForId(nodeId); 343 if (node && (node->nodeType() == Node::TEXT_NODE)) { 344 Text* text_node = static_cast<Text*>(node); 345 ExceptionCode ec = 0; 346 text_node->replaceWholeText(value, ec); 347 m_frontend->didApplyDomChange(callId, ec == 0); 348 } else { 349 m_frontend->didApplyDomChange(callId, false); 350 } 351 } 352 353 void InspectorDOMAgent::getEventListenersForNode(long callId, long nodeId) 354 { 355 Node* node = nodeForId(nodeId); 356 ScriptArray listenersArray = m_frontend->newScriptArray(); 357 unsigned counter = 0; 358 EventTargetData* d; 359 360 // Quick break if a null node or no listeners at all 361 if (!node || !(d = node->eventTargetData())) { 362 m_frontend->didGetEventListenersForNode(callId, nodeId, listenersArray); 363 return; 364 } 365 366 // Get the list of event types this Node is concerned with 367 Vector<AtomicString> eventTypes; 368 const EventListenerMap& listenerMap = d->eventListenerMap; 369 EventListenerMap::const_iterator end = listenerMap.end(); 370 for (EventListenerMap::const_iterator iter = listenerMap.begin(); iter != end; ++iter) 371 eventTypes.append(iter->first); 372 373 // Quick break if no useful listeners 374 size_t eventTypesLength = eventTypes.size(); 375 if (eventTypesLength == 0) { 376 m_frontend->didGetEventListenersForNode(callId, nodeId, listenersArray); 377 return; 378 } 379 380 // The Node's Event Ancestors (not including self) 381 Vector<RefPtr<ContainerNode> > ancestors; 382 node->eventAncestors(ancestors); 383 384 // Nodes and their Listeners for the concerned event types (order is top to bottom) 385 Vector<EventListenerInfo> eventInformation; 386 for (size_t i = ancestors.size(); i; --i) { 387 ContainerNode* ancestor = ancestors[i - 1].get(); 388 for (size_t j = 0; j < eventTypesLength; ++j) { 389 AtomicString& type = eventTypes[j]; 390 if (ancestor->hasEventListeners(type)) 391 eventInformation.append(EventListenerInfo(static_cast<Node*>(ancestor), type, ancestor->getEventListeners(type))); 392 } 393 } 394 395 // Insert the Current Node at the end of that list (last in capturing, first in bubbling) 396 for (size_t i = 0; i < eventTypesLength; ++i) { 397 const AtomicString& type = eventTypes[i]; 398 eventInformation.append(EventListenerInfo(node, type, node->getEventListeners(type))); 399 } 400 401 // Get Capturing Listeners (in this order) 402 size_t eventInformationLength = eventInformation.size(); 403 for (size_t i = 0; i < eventInformationLength; ++i) { 404 const EventListenerInfo& info = eventInformation[i]; 405 const EventListenerVector& vector = info.eventListenerVector; 406 for (size_t j = 0; j < vector.size(); ++j) { 407 const RegisteredEventListener& listener = vector[j]; 408 if (listener.useCapture) 409 listenersArray.set(counter++, buildObjectForEventListener(listener, info.eventType, info.node)); 410 } 411 } 412 413 // Get Bubbling Listeners (reverse order) 414 for (size_t i = eventInformationLength; i; --i) { 415 const EventListenerInfo& info = eventInformation[i - 1]; 416 const EventListenerVector& vector = info.eventListenerVector; 417 for (size_t j = 0; j < vector.size(); ++j) { 418 const RegisteredEventListener& listener = vector[j]; 419 if (!listener.useCapture) 420 listenersArray.set(counter++, buildObjectForEventListener(listener, info.eventType, info.node)); 421 } 422 } 423 424 m_frontend->didGetEventListenersForNode(callId, nodeId, listenersArray); 425 } 426 427 String InspectorDOMAgent::documentURLString(Document* document) const 428 { 429 if (!document || document->url().isNull()) 430 return ""; 431 return document->url().string(); 432 } 433 434 ScriptObject InspectorDOMAgent::buildObjectForNode(Node* node, int depth, NodeToIdMap* nodesMap) 435 { 436 ScriptObject value = m_frontend->newScriptObject(); 437 438 long id = bind(node, nodesMap); 439 String nodeName; 440 String localName; 441 String nodeValue; 442 443 switch (node->nodeType()) { 444 case Node::TEXT_NODE: 445 case Node::COMMENT_NODE: 446 nodeValue = node->nodeValue(); 447 break; 448 case Node::ATTRIBUTE_NODE: 449 localName = node->localName(); 450 break; 451 case Node::DOCUMENT_FRAGMENT_NODE: 452 break; 453 case Node::DOCUMENT_NODE: 454 case Node::ELEMENT_NODE: 455 default: 456 nodeName = node->nodeName(); 457 localName = node->localName(); 458 break; 459 } 460 461 value.set("id", id); 462 value.set("nodeType", node->nodeType()); 463 value.set("nodeName", nodeName); 464 value.set("localName", localName); 465 value.set("nodeValue", nodeValue); 466 467 if (node->nodeType() == Node::ELEMENT_NODE || node->nodeType() == Node::DOCUMENT_NODE || node->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) { 468 int nodeCount = innerChildNodeCount(node); 469 value.set("childNodeCount", nodeCount); 470 ScriptArray children = buildArrayForContainerChildren(node, depth, nodesMap); 471 if (children.length() > 0) 472 value.set("children", children); 473 474 if (node->nodeType() == Node::ELEMENT_NODE) { 475 Element* element = static_cast<Element*>(node); 476 value.set("attributes", buildArrayForElementAttributes(element)); 477 if (node->isFrameOwnerElement()) { 478 HTMLFrameOwnerElement* frameOwner = static_cast<HTMLFrameOwnerElement*>(node); 479 value.set("documentURL", documentURLString(frameOwner->contentDocument())); 480 } 481 } else if (node->nodeType() == Node::DOCUMENT_NODE) { 482 Document* document = static_cast<Document*>(node); 483 value.set("documentURL", documentURLString(document)); 484 } 485 } else if (node->nodeType() == Node::DOCUMENT_TYPE_NODE) { 486 DocumentType* docType = static_cast<DocumentType*>(node); 487 value.set("publicId", docType->publicId()); 488 value.set("systemId", docType->systemId()); 489 value.set("internalSubset", docType->internalSubset()); 490 } 491 return value; 492 } 493 494 ScriptArray InspectorDOMAgent::buildArrayForElementAttributes(Element* element) 495 { 496 ScriptArray attributesValue = m_frontend->newScriptArray(); 497 // Go through all attributes and serialize them. 498 const NamedNodeMap* attrMap = element->attributes(true); 499 if (!attrMap) 500 return attributesValue; 501 unsigned numAttrs = attrMap->length(); 502 int index = 0; 503 for (unsigned i = 0; i < numAttrs; ++i) { 504 // Add attribute pair 505 const Attribute *attribute = attrMap->attributeItem(i); 506 attributesValue.set(index++, attribute->name().toString()); 507 attributesValue.set(index++, attribute->value()); 508 } 509 return attributesValue; 510 } 511 512 ScriptArray InspectorDOMAgent::buildArrayForContainerChildren(Node* container, int depth, NodeToIdMap* nodesMap) 513 { 514 ScriptArray children = m_frontend->newScriptArray(); 515 if (depth == 0) { 516 int index = 0; 517 // Special case the_only text child. 518 if (innerChildNodeCount(container) == 1) { 519 Node *child = innerFirstChild(container); 520 if (child->nodeType() == Node::TEXT_NODE) 521 children.set(index++, buildObjectForNode(child, 0, nodesMap)); 522 } 523 return children; 524 } else if (depth > 0) { 525 depth--; 526 } 527 528 int index = 0; 529 for (Node *child = innerFirstChild(container); child; child = innerNextSibling(child)) 530 children.set(index++, buildObjectForNode(child, depth, nodesMap)); 531 return children; 532 } 533 534 ScriptObject InspectorDOMAgent::buildObjectForEventListener(const RegisteredEventListener& registeredEventListener, const AtomicString& eventType, Node* node) 535 { 536 RefPtr<EventListener> eventListener = registeredEventListener.listener; 537 ScriptObject value = m_frontend->newScriptObject(); 538 value.set("type", eventType); 539 value.set("useCapture", registeredEventListener.useCapture); 540 value.set("isAttribute", eventListener->isAttribute()); 541 value.set("nodeId", pushNodePathToFrontend(node)); 542 value.set("listener", getEventListenerHandlerBody(node->document(), m_frontend->scriptState(), eventListener.get())); 543 return value; 544 } 545 546 Node* InspectorDOMAgent::innerFirstChild(Node* node) 547 { 548 if (node->isFrameOwnerElement()) { 549 HTMLFrameOwnerElement* frameOwner = static_cast<HTMLFrameOwnerElement*>(node); 550 Document* doc = frameOwner->contentDocument(); 551 if (doc) { 552 startListening(doc); 553 return doc->firstChild(); 554 } 555 } 556 node = node->firstChild(); 557 while (isWhitespace(node)) 558 node = node->nextSibling(); 559 return node; 560 } 561 562 Node* InspectorDOMAgent::innerNextSibling(Node* node) 563 { 564 do { 565 node = node->nextSibling(); 566 } while (isWhitespace(node)); 567 return node; 568 } 569 570 Node* InspectorDOMAgent::innerPreviousSibling(Node* node) 571 { 572 do { 573 node = node->previousSibling(); 574 } while (isWhitespace(node)); 575 return node; 576 } 577 578 unsigned InspectorDOMAgent::innerChildNodeCount(Node* node) 579 { 580 unsigned count = 0; 581 Node* child = innerFirstChild(node); 582 while (child) { 583 count++; 584 child = innerNextSibling(child); 585 } 586 return count; 587 } 588 589 Node* InspectorDOMAgent::innerParentNode(Node* node) 590 { 591 Node* parent = node->parentNode(); 592 if (parent && parent->nodeType() == Node::DOCUMENT_NODE) 593 return static_cast<Document*>(parent)->ownerElement(); 594 return parent; 595 } 596 597 bool InspectorDOMAgent::isWhitespace(Node* node) 598 { 599 //TODO: pull ignoreWhitespace setting from the frontend and use here. 600 return node && node->nodeType() == Node::TEXT_NODE && node->nodeValue().stripWhiteSpace().length() == 0; 601 } 602 603 Document* InspectorDOMAgent::mainFrameDocument() const 604 { 605 ListHashSet<RefPtr<Document> >::const_iterator it = m_documents.begin(); 606 if (it != m_documents.end()) 607 return it->get(); 608 return 0; 609 } 610 611 bool InspectorDOMAgent::operator==(const EventListener& listener) 612 { 613 if (const InspectorDOMAgent* inspectorDOMAgentListener = InspectorDOMAgent::cast(&listener)) 614 return mainFrameDocument() == inspectorDOMAgentListener->mainFrameDocument(); 615 return false; 616 } 617 618 void InspectorDOMAgent::didInsertDOMNode(Node* node) 619 { 620 if (isWhitespace(node)) 621 return; 622 623 // We could be attaching existing subtree. Forget the bindings. 624 unbind(node, &m_documentNodeToIdMap); 625 626 Node* parent = node->parentNode(); 627 long parentId = m_documentNodeToIdMap.get(parent); 628 // Return if parent is not mapped yet. 629 if (!parentId) 630 return; 631 632 if (!m_childrenRequested.contains(parentId)) { 633 // No children are mapped yet -> only notify on changes of hasChildren. 634 m_frontend->childNodeCountUpdated(parentId, innerChildNodeCount(parent)); 635 } else { 636 // Children have been requested -> return value of a new child. 637 Node* prevSibling = innerPreviousSibling(node); 638 long prevId = prevSibling ? m_documentNodeToIdMap.get(prevSibling) : 0; 639 ScriptObject value = buildObjectForNode(node, 0, &m_documentNodeToIdMap); 640 m_frontend->childNodeInserted(parentId, prevId, value); 641 } 642 } 643 644 void InspectorDOMAgent::didRemoveDOMNode(Node* node) 645 { 646 if (isWhitespace(node)) 647 return; 648 649 Node* parent = node->parentNode(); 650 long parentId = m_documentNodeToIdMap.get(parent); 651 // If parent is not mapped yet -> ignore the event. 652 if (!parentId) 653 return; 654 655 if (!m_childrenRequested.contains(parentId)) { 656 // No children are mapped yet -> only notify on changes of hasChildren. 657 if (innerChildNodeCount(parent) == 1) 658 m_frontend->childNodeCountUpdated(parentId, 0); 659 } else 660 m_frontend->childNodeRemoved(parentId, m_documentNodeToIdMap.get(node)); 661 unbind(node, &m_documentNodeToIdMap); 662 } 663 664 void InspectorDOMAgent::didModifyDOMAttr(Element* element) 665 { 666 long id = m_documentNodeToIdMap.get(element); 667 // If node is not mapped yet -> ignore the event. 668 if (!id) 669 return; 670 671 m_frontend->attributesUpdated(id, buildArrayForElementAttributes(element)); 672 } 673 674 } // namespace WebCore 675 676 #endif // ENABLE(INSPECTOR) 677