1 /* 2 * Copyright (C) 2009 Apple Inc. All rights reserved. 3 * Copyright (C) 2011 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 "Attr.h" 37 #include "CSSComputedStyleDeclaration.h" 38 #include "CSSMutableStyleDeclaration.h" 39 #include "CSSPropertyNames.h" 40 #include "CSSPropertySourceData.h" 41 #include "CSSRule.h" 42 #include "CSSRuleList.h" 43 #include "CSSStyleRule.h" 44 #include "CSSStyleSelector.h" 45 #include "CSSStyleSheet.h" 46 #include "CharacterData.h" 47 #include "ContainerNode.h" 48 #include "Cookie.h" 49 #include "CookieJar.h" 50 #include "DOMNodeHighlighter.h" 51 #include "DOMWindow.h" 52 #include "Document.h" 53 #include "DocumentType.h" 54 #include "Event.h" 55 #include "EventContext.h" 56 #include "EventListener.h" 57 #include "EventNames.h" 58 #include "EventTarget.h" 59 #include "Frame.h" 60 #include "FrameTree.h" 61 #include "HitTestResult.h" 62 #include "HTMLElement.h" 63 #include "HTMLFrameOwnerElement.h" 64 #include "InjectedScriptManager.h" 65 #include "InspectorClient.h" 66 #include "InspectorFrontend.h" 67 #include "InspectorResourceAgent.h" 68 #include "InspectorState.h" 69 #include "InstrumentingAgents.h" 70 #include "MutationEvent.h" 71 #include "Node.h" 72 #include "NodeList.h" 73 #include "Page.h" 74 #include "Pasteboard.h" 75 #include "PlatformString.h" 76 #include "RenderStyle.h" 77 #include "RenderStyleConstants.h" 78 #include "ScriptEventListener.h" 79 #include "StyleSheetList.h" 80 #include "Text.h" 81 82 #if ENABLE(XPATH) 83 #include "XPathResult.h" 84 #endif 85 86 #include "markup.h" 87 88 #include <wtf/text/CString.h> 89 #include <wtf/text/StringConcatenate.h> 90 #include <wtf/HashSet.h> 91 #include <wtf/ListHashSet.h> 92 #include <wtf/OwnPtr.h> 93 #include <wtf/Vector.h> 94 #include <wtf/text/AtomicString.h> 95 96 namespace WebCore { 97 98 namespace DOMAgentState { 99 static const char documentRequested[] = "documentRequested"; 100 }; 101 102 class MatchJob { 103 public: 104 virtual void match(ListHashSet<Node*>& resultCollector) = 0; 105 virtual ~MatchJob() { } 106 107 protected: 108 MatchJob(Document* document, const String& query) 109 : m_document(document) 110 , m_query(query) { } 111 112 void addNodesToResults(PassRefPtr<NodeList> nodes, ListHashSet<Node*>& resultCollector) 113 { 114 for (unsigned i = 0; nodes && i < nodes->length(); ++i) 115 resultCollector.add(nodes->item(i)); 116 } 117 118 RefPtr<Document> m_document; 119 String m_query; 120 }; 121 122 class RevalidateStyleAttributeTask { 123 public: 124 RevalidateStyleAttributeTask(InspectorDOMAgent*); 125 void scheduleFor(Element*); 126 void reset() { m_timer.stop(); } 127 void onTimer(Timer<RevalidateStyleAttributeTask>*); 128 129 private: 130 InspectorDOMAgent* m_domAgent; 131 Timer<RevalidateStyleAttributeTask> m_timer; 132 HashSet<RefPtr<Element> > m_elements; 133 }; 134 135 namespace { 136 137 class MatchExactIdJob : public WebCore::MatchJob { 138 public: 139 MatchExactIdJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { } 140 virtual ~MatchExactIdJob() { } 141 142 protected: 143 virtual void match(ListHashSet<Node*>& resultCollector) 144 { 145 if (m_query.isEmpty()) 146 return; 147 148 Element* element = m_document->getElementById(m_query); 149 if (element) 150 resultCollector.add(element); 151 } 152 }; 153 154 class MatchExactClassNamesJob : public WebCore::MatchJob { 155 public: 156 MatchExactClassNamesJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { } 157 virtual ~MatchExactClassNamesJob() { } 158 159 virtual void match(ListHashSet<Node*>& resultCollector) 160 { 161 if (!m_query.isEmpty()) 162 addNodesToResults(m_document->getElementsByClassName(m_query), resultCollector); 163 } 164 }; 165 166 class MatchExactTagNamesJob : public WebCore::MatchJob { 167 public: 168 MatchExactTagNamesJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { } 169 virtual ~MatchExactTagNamesJob() { } 170 171 virtual void match(ListHashSet<Node*>& resultCollector) 172 { 173 if (!m_query.isEmpty()) 174 addNodesToResults(m_document->getElementsByName(m_query), resultCollector); 175 } 176 }; 177 178 class MatchQuerySelectorAllJob : public WebCore::MatchJob { 179 public: 180 MatchQuerySelectorAllJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { } 181 virtual ~MatchQuerySelectorAllJob() { } 182 183 virtual void match(ListHashSet<Node*>& resultCollector) 184 { 185 if (m_query.isEmpty()) 186 return; 187 188 ExceptionCode ec = 0; 189 RefPtr<NodeList> list = m_document->querySelectorAll(m_query, ec); 190 if (!ec) 191 addNodesToResults(list, resultCollector); 192 } 193 }; 194 195 class MatchXPathJob : public WebCore::MatchJob { 196 public: 197 MatchXPathJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { } 198 virtual ~MatchXPathJob() { } 199 200 virtual void match(ListHashSet<Node*>& resultCollector) 201 { 202 #if ENABLE(XPATH) 203 if (m_query.isEmpty()) 204 return; 205 206 ExceptionCode ec = 0; 207 RefPtr<XPathResult> result = m_document->evaluate(m_query, m_document.get(), 0, XPathResult::ORDERED_NODE_SNAPSHOT_TYPE, 0, ec); 208 if (ec || !result) 209 return; 210 211 unsigned long size = result->snapshotLength(ec); 212 for (unsigned long i = 0; !ec && i < size; ++i) { 213 Node* node = result->snapshotItem(i, ec); 214 if (ec) 215 break; 216 217 if (node->nodeType() == Node::ATTRIBUTE_NODE) 218 node = static_cast<Attr*>(node)->ownerElement(); 219 resultCollector.add(node); 220 } 221 #else 222 UNUSED_PARAM(resultCollector); 223 #endif 224 } 225 }; 226 227 class MatchPlainTextJob : public MatchXPathJob { 228 public: 229 MatchPlainTextJob(Document* document, const String& query) : MatchXPathJob(document, query) 230 { 231 m_query = "//text()[contains(., '" + m_query + "')] | //comment()[contains(., '" + m_query + "')]"; 232 } 233 virtual ~MatchPlainTextJob() { } 234 }; 235 236 } 237 238 RevalidateStyleAttributeTask::RevalidateStyleAttributeTask(InspectorDOMAgent* domAgent) 239 : m_domAgent(domAgent) 240 , m_timer(this, &RevalidateStyleAttributeTask::onTimer) 241 { 242 } 243 244 void RevalidateStyleAttributeTask::scheduleFor(Element* element) 245 { 246 m_elements.add(element); 247 if (!m_timer.isActive()) 248 m_timer.startOneShot(0); 249 } 250 251 void RevalidateStyleAttributeTask::onTimer(Timer<RevalidateStyleAttributeTask>*) 252 { 253 // The timer is stopped on m_domAgent destruction, so this method will never be called after m_domAgent has been destroyed. 254 for (HashSet<RefPtr<Element> >::iterator it = m_elements.begin(), end = m_elements.end(); it != end; ++it) 255 m_domAgent->didModifyDOMAttr(it->get()); 256 257 m_elements.clear(); 258 } 259 260 InspectorDOMAgent::InspectorDOMAgent(InstrumentingAgents* instrumentingAgents, Page* inspectedPage, InspectorClient* client, InspectorState* inspectorState, InjectedScriptManager* injectedScriptManager) 261 : m_instrumentingAgents(instrumentingAgents) 262 , m_inspectedPage(inspectedPage) 263 , m_client(client) 264 , m_inspectorState(inspectorState) 265 , m_injectedScriptManager(injectedScriptManager) 266 , m_frontend(0) 267 , m_domListener(0) 268 , m_lastNodeId(1) 269 , m_matchJobsTimer(this, &InspectorDOMAgent::onMatchJobsTimer) 270 , m_searchingForNode(false) 271 { 272 } 273 274 InspectorDOMAgent::~InspectorDOMAgent() 275 { 276 reset(); 277 ASSERT(!m_highlightedNode); 278 ASSERT(!m_searchingForNode); 279 } 280 281 void InspectorDOMAgent::setFrontend(InspectorFrontend* frontend) 282 { 283 ASSERT(!m_frontend); 284 m_frontend = frontend->dom(); 285 m_instrumentingAgents->setInspectorDOMAgent(this); 286 m_document = m_inspectedPage->mainFrame()->document(); 287 288 if (m_nodeToFocus) 289 focusNode(); 290 } 291 292 void InspectorDOMAgent::clearFrontend() 293 { 294 ASSERT(m_frontend); 295 setSearchingForNode(false); 296 297 ErrorString error; 298 hideHighlight(&error); 299 300 m_frontend = 0; 301 m_instrumentingAgents->setInspectorDOMAgent(0); 302 m_inspectorState->setBoolean(DOMAgentState::documentRequested, false); 303 reset(); 304 } 305 306 void InspectorDOMAgent::restore() 307 { 308 // Reset document to avoid early return from setDocument. 309 m_document = 0; 310 setDocument(m_inspectedPage->mainFrame()->document()); 311 } 312 313 Vector<Document*> InspectorDOMAgent::documents() 314 { 315 Vector<Document*> result; 316 for (Frame* frame = m_document->frame(); frame; frame = frame->tree()->traverseNext()) { 317 Document* document = frame->document(); 318 if (!document) 319 continue; 320 result.append(document); 321 } 322 return result; 323 } 324 325 void InspectorDOMAgent::reset() 326 { 327 ErrorString error; 328 cancelSearch(&error); 329 discardBindings(); 330 if (m_revalidateStyleAttrTask) 331 m_revalidateStyleAttrTask->reset(); 332 m_document = 0; 333 } 334 335 void InspectorDOMAgent::setDOMListener(DOMListener* listener) 336 { 337 m_domListener = listener; 338 } 339 340 void InspectorDOMAgent::setDocument(Document* doc) 341 { 342 if (doc == m_document.get()) 343 return; 344 345 reset(); 346 347 m_document = doc; 348 349 if (!m_inspectorState->getBoolean(DOMAgentState::documentRequested)) 350 return; 351 352 // Immediately communicate 0 document or document that has finished loading. 353 if (!doc || !doc->parsing()) 354 m_frontend->documentUpdated(); 355 } 356 357 void InspectorDOMAgent::releaseDanglingNodes() 358 { 359 deleteAllValues(m_danglingNodeToIdMaps); 360 m_danglingNodeToIdMaps.clear(); 361 } 362 363 int InspectorDOMAgent::bind(Node* node, NodeToIdMap* nodesMap) 364 { 365 int id = nodesMap->get(node); 366 if (id) 367 return id; 368 id = m_lastNodeId++; 369 nodesMap->set(node, id); 370 m_idToNode.set(id, node); 371 m_idToNodesMap.set(id, nodesMap); 372 return id; 373 } 374 375 void InspectorDOMAgent::unbind(Node* node, NodeToIdMap* nodesMap) 376 { 377 if (node->isFrameOwnerElement()) { 378 const HTMLFrameOwnerElement* frameOwner = static_cast<const HTMLFrameOwnerElement*>(node); 379 if (m_domListener) 380 m_domListener->didRemoveDocument(frameOwner->contentDocument()); 381 } 382 383 int id = nodesMap->get(node); 384 if (!id) 385 return; 386 m_idToNode.remove(id); 387 nodesMap->remove(node); 388 bool childrenRequested = m_childrenRequested.contains(id); 389 if (childrenRequested) { 390 // Unbind subtree known to client recursively. 391 m_childrenRequested.remove(id); 392 Node* child = innerFirstChild(node); 393 while (child) { 394 unbind(child, nodesMap); 395 child = innerNextSibling(child); 396 } 397 } 398 } 399 400 Node* InspectorDOMAgent::assertNode(ErrorString* errorString, int nodeId) 401 { 402 Node* node = nodeForId(nodeId); 403 if (!node) { 404 *errorString = "Could not find node with given id"; 405 return 0; 406 } 407 return node; 408 } 409 410 Element* InspectorDOMAgent::assertElement(ErrorString* errorString, int nodeId) 411 { 412 Node* node = assertNode(errorString, nodeId); 413 if (!node) 414 return 0; 415 416 if (node->nodeType() != Node::ELEMENT_NODE) { 417 *errorString = "Node is not an Element"; 418 return 0; 419 } 420 return toElement(node); 421 } 422 423 424 HTMLElement* InspectorDOMAgent::assertHTMLElement(ErrorString* errorString, int nodeId) 425 { 426 Element* element = assertElement(errorString, nodeId); 427 if (!element) 428 return 0; 429 430 if (!element->isHTMLElement()) { 431 *errorString = "Node is not an HTML Element"; 432 return 0; 433 } 434 return toHTMLElement(element); 435 } 436 437 void InspectorDOMAgent::getDocument(ErrorString*, RefPtr<InspectorObject>* root) 438 { 439 m_inspectorState->setBoolean(DOMAgentState::documentRequested, true); 440 441 if (!m_document) 442 return; 443 444 // Reset backend state. 445 RefPtr<Document> doc = m_document; 446 reset(); 447 m_document = doc; 448 449 *root = buildObjectForNode(m_document.get(), 2, &m_documentNodeToIdMap); 450 } 451 452 void InspectorDOMAgent::pushChildNodesToFrontend(int nodeId) 453 { 454 Node* node = nodeForId(nodeId); 455 if (!node || (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE && node->nodeType() != Node::DOCUMENT_FRAGMENT_NODE)) 456 return; 457 if (m_childrenRequested.contains(nodeId)) 458 return; 459 460 NodeToIdMap* nodeMap = m_idToNodesMap.get(nodeId); 461 RefPtr<InspectorArray> children = buildArrayForContainerChildren(node, 1, nodeMap); 462 m_frontend->setChildNodes(nodeId, children.release()); 463 } 464 465 void InspectorDOMAgent::discardBindings() 466 { 467 m_documentNodeToIdMap.clear(); 468 m_idToNode.clear(); 469 releaseDanglingNodes(); 470 m_childrenRequested.clear(); 471 } 472 473 Node* InspectorDOMAgent::nodeForId(int id) 474 { 475 if (!id) 476 return 0; 477 478 HashMap<int, Node*>::iterator it = m_idToNode.find(id); 479 if (it != m_idToNode.end()) 480 return it->second; 481 return 0; 482 } 483 484 void InspectorDOMAgent::getChildNodes(ErrorString*, int nodeId) 485 { 486 pushChildNodesToFrontend(nodeId); 487 } 488 489 void InspectorDOMAgent::querySelector(ErrorString* errorString, int nodeId, const String& selectors, int* elementId) 490 { 491 *elementId = 0; 492 Node* node = assertNode(errorString, nodeId); 493 if (!node) 494 return; 495 496 ExceptionCode ec = 0; 497 RefPtr<Element> element = node->querySelector(selectors, ec); 498 if (ec) { 499 *errorString = "DOM Error while querying"; 500 return; 501 } 502 503 if (element) 504 *elementId = pushNodePathToFrontend(element.get()); 505 } 506 507 void InspectorDOMAgent::querySelectorAll(ErrorString* errorString, int nodeId, const String& selectors, RefPtr<InspectorArray>* result) 508 { 509 Node* node = assertNode(errorString, nodeId); 510 if (!node) 511 return; 512 513 ExceptionCode ec = 0; 514 RefPtr<NodeList> nodes = node->querySelectorAll(selectors, ec); 515 if (ec) { 516 *errorString = "DOM Error while querying"; 517 return; 518 } 519 520 for (unsigned i = 0; i < nodes->length(); ++i) 521 (*result)->pushNumber(pushNodePathToFrontend(nodes->item(i))); 522 } 523 524 int InspectorDOMAgent::pushNodePathToFrontend(Node* nodeToPush) 525 { 526 ASSERT(nodeToPush); // Invalid input 527 528 if (!m_document) 529 return 0; 530 if (!m_documentNodeToIdMap.contains(m_document)) 531 return 0; 532 533 // Return id in case the node is known. 534 int result = m_documentNodeToIdMap.get(nodeToPush); 535 if (result) 536 return result; 537 538 Node* node = nodeToPush; 539 Vector<Node*> path; 540 NodeToIdMap* danglingMap = 0; 541 542 while (true) { 543 Node* parent = innerParentNode(node); 544 if (!parent) { 545 // Node being pushed is detached -> push subtree root. 546 danglingMap = new NodeToIdMap(); 547 m_danglingNodeToIdMaps.append(danglingMap); 548 RefPtr<InspectorArray> children = InspectorArray::create(); 549 children->pushObject(buildObjectForNode(node, 0, danglingMap)); 550 m_frontend->setChildNodes(0, children); 551 break; 552 } else { 553 path.append(parent); 554 if (m_documentNodeToIdMap.get(parent)) 555 break; 556 else 557 node = parent; 558 } 559 } 560 561 NodeToIdMap* map = danglingMap ? danglingMap : &m_documentNodeToIdMap; 562 for (int i = path.size() - 1; i >= 0; --i) { 563 int nodeId = map->get(path.at(i)); 564 ASSERT(nodeId); 565 pushChildNodesToFrontend(nodeId); 566 } 567 return map->get(nodeToPush); 568 } 569 570 int InspectorDOMAgent::boundNodeId(Node* node) 571 { 572 return m_documentNodeToIdMap.get(node); 573 } 574 575 void InspectorDOMAgent::setAttribute(ErrorString* errorString, int elementId, const String& name, const String& value) 576 { 577 Element* element = assertElement(errorString, elementId); 578 if (element) { 579 ExceptionCode ec = 0; 580 element->setAttribute(name, value, ec); 581 if (ec) 582 *errorString = "Exception while setting attribute value"; 583 } 584 } 585 586 void InspectorDOMAgent::removeAttribute(ErrorString* errorString, int elementId, const String& name) 587 { 588 Element* element = assertElement(errorString, elementId); 589 if (element) { 590 ExceptionCode ec = 0; 591 element->removeAttribute(name, ec); 592 if (ec) 593 *errorString = "Exception while removing attribute"; 594 } 595 } 596 597 void InspectorDOMAgent::removeNode(ErrorString* errorString, int nodeId) 598 { 599 Node* node = assertNode(errorString, nodeId); 600 if (!node) 601 return; 602 603 ContainerNode* parentNode = node->parentNode(); 604 if (!parentNode) { 605 *errorString = "Can not remove detached node"; 606 return; 607 } 608 609 ExceptionCode ec = 0; 610 parentNode->removeChild(node, ec); 611 if (ec) 612 *errorString = "Could not remove node due to DOM exception"; 613 } 614 615 void InspectorDOMAgent::setNodeName(ErrorString*, int nodeId, const String& tagName, int* newId) 616 { 617 *newId = 0; 618 619 Node* oldNode = nodeForId(nodeId); 620 if (!oldNode || !oldNode->isElementNode()) 621 return; 622 623 ExceptionCode ec = 0; 624 RefPtr<Element> newElem = oldNode->document()->createElement(tagName, ec); 625 if (ec) 626 return; 627 628 // Copy over the original node's attributes. 629 Element* oldElem = static_cast<Element*>(oldNode); 630 newElem->copyNonAttributeProperties(oldElem); 631 if (oldElem->attributes()) 632 newElem->attributes()->setAttributes(*(oldElem->attributes(true))); 633 634 // Copy over the original node's children. 635 Node* child; 636 while ((child = oldNode->firstChild())) 637 newElem->appendChild(child, ec); 638 639 // Replace the old node with the new node 640 ContainerNode* parent = oldNode->parentNode(); 641 parent->insertBefore(newElem, oldNode->nextSibling(), ec); 642 parent->removeChild(oldNode, ec); 643 644 if (ec) 645 return; 646 647 *newId = pushNodePathToFrontend(newElem.get()); 648 if (m_childrenRequested.contains(nodeId)) 649 pushChildNodesToFrontend(*newId); 650 } 651 652 void InspectorDOMAgent::getOuterHTML(ErrorString* errorString, int nodeId, WTF::String* outerHTML) 653 { 654 HTMLElement* element = assertHTMLElement(errorString, nodeId); 655 if (element) 656 *outerHTML = element->outerHTML(); 657 } 658 659 void InspectorDOMAgent::setOuterHTML(ErrorString* errorString, int nodeId, const String& outerHTML, int* newId) 660 { 661 HTMLElement* htmlElement = assertHTMLElement(errorString, nodeId); 662 if (!htmlElement) 663 return; 664 665 bool requiresTotalUpdate = htmlElement->tagName() == "HTML" || htmlElement->tagName() == "BODY" || htmlElement->tagName() == "HEAD"; 666 667 bool childrenRequested = m_childrenRequested.contains(nodeId); 668 Node* previousSibling = htmlElement->previousSibling(); 669 ContainerNode* parentNode = htmlElement->parentNode(); 670 671 ExceptionCode ec = 0; 672 htmlElement->setOuterHTML(outerHTML, ec); 673 if (ec) 674 return; 675 676 if (requiresTotalUpdate) { 677 RefPtr<Document> document = m_document; 678 reset(); 679 setDocument(document.get()); 680 *newId = 0; 681 return; 682 } 683 684 Node* newNode = previousSibling ? previousSibling->nextSibling() : parentNode->firstChild(); 685 if (!newNode) { 686 // The only child node has been deleted. 687 *newId = 0; 688 return; 689 } 690 691 *newId = pushNodePathToFrontend(newNode); 692 if (childrenRequested) 693 pushChildNodesToFrontend(*newId); 694 } 695 696 void InspectorDOMAgent::setNodeValue(ErrorString* errorString, int nodeId, const String& value) 697 { 698 Node* node = assertNode(errorString, nodeId); 699 if (!node) 700 return; 701 702 if (node->nodeType() != Node::TEXT_NODE) { 703 *errorString = "Can only set value of text nodes"; 704 return; 705 } 706 707 Text* textNode = static_cast<Text*>(node); 708 ExceptionCode ec = 0; 709 textNode->replaceWholeText(value, ec); 710 if (ec) 711 *errorString = "DOM Error while setting the node value"; 712 } 713 714 void InspectorDOMAgent::getEventListenersForNode(ErrorString*, int nodeId, RefPtr<InspectorArray>* listenersArray) 715 { 716 Node* node = nodeForId(nodeId); 717 EventTargetData* d; 718 719 // Quick break if a null node or no listeners at all 720 if (!node || !(d = node->eventTargetData())) 721 return; 722 723 // Get the list of event types this Node is concerned with 724 Vector<AtomicString> eventTypes; 725 const EventListenerMap& listenerMap = d->eventListenerMap; 726 EventListenerMap::const_iterator end = listenerMap.end(); 727 for (EventListenerMap::const_iterator iter = listenerMap.begin(); iter != end; ++iter) 728 eventTypes.append(iter->first); 729 730 // Quick break if no useful listeners 731 size_t eventTypesLength = eventTypes.size(); 732 if (!eventTypesLength) 733 return; 734 735 // The Node's Ancestors (not including self) 736 Vector<ContainerNode*> ancestors; 737 for (ContainerNode* ancestor = node->parentOrHostNode(); ancestor; ancestor = ancestor->parentOrHostNode()) 738 ancestors.append(ancestor); 739 740 // Nodes and their Listeners for the concerned event types (order is top to bottom) 741 Vector<EventListenerInfo> eventInformation; 742 for (size_t i = ancestors.size(); i; --i) { 743 ContainerNode* ancestor = ancestors[i - 1]; 744 for (size_t j = 0; j < eventTypesLength; ++j) { 745 AtomicString& type = eventTypes[j]; 746 if (ancestor->hasEventListeners(type)) 747 eventInformation.append(EventListenerInfo(ancestor, type, ancestor->getEventListeners(type))); 748 } 749 } 750 751 // Insert the Current Node at the end of that list (last in capturing, first in bubbling) 752 for (size_t i = 0; i < eventTypesLength; ++i) { 753 const AtomicString& type = eventTypes[i]; 754 eventInformation.append(EventListenerInfo(node, type, node->getEventListeners(type))); 755 } 756 757 // Get Capturing Listeners (in this order) 758 size_t eventInformationLength = eventInformation.size(); 759 for (size_t i = 0; i < eventInformationLength; ++i) { 760 const EventListenerInfo& info = eventInformation[i]; 761 const EventListenerVector& vector = info.eventListenerVector; 762 for (size_t j = 0; j < vector.size(); ++j) { 763 const RegisteredEventListener& listener = vector[j]; 764 if (listener.useCapture) 765 (*listenersArray)->pushObject(buildObjectForEventListener(listener, info.eventType, info.node)); 766 } 767 } 768 769 // Get Bubbling Listeners (reverse order) 770 for (size_t i = eventInformationLength; i; --i) { 771 const EventListenerInfo& info = eventInformation[i - 1]; 772 const EventListenerVector& vector = info.eventListenerVector; 773 for (size_t j = 0; j < vector.size(); ++j) { 774 const RegisteredEventListener& listener = vector[j]; 775 if (!listener.useCapture) 776 (*listenersArray)->pushObject(buildObjectForEventListener(listener, info.eventType, info.node)); 777 } 778 } 779 } 780 781 void InspectorDOMAgent::performSearch(ErrorString* error, const String& whitespaceTrimmedQuery, const bool* const runSynchronously) 782 { 783 // FIXME: Few things are missing here: 784 // 1) Search works with node granularity - number of matches within node is not calculated. 785 // 2) There is no need to push all search results to the front-end at a time, pushing next / previous result 786 // is sufficient. 787 788 unsigned queryLength = whitespaceTrimmedQuery.length(); 789 bool startTagFound = !whitespaceTrimmedQuery.find('<'); 790 bool endTagFound = whitespaceTrimmedQuery.reverseFind('>') + 1 == queryLength; 791 792 String tagNameQuery = whitespaceTrimmedQuery; 793 if (startTagFound || endTagFound) 794 tagNameQuery = tagNameQuery.substring(startTagFound ? 1 : 0, endTagFound ? queryLength - 1 : queryLength); 795 if (!Document::isValidName(tagNameQuery)) 796 tagNameQuery = ""; 797 798 String attributeNameQuery = whitespaceTrimmedQuery; 799 if (!Document::isValidName(attributeNameQuery)) 800 attributeNameQuery = ""; 801 802 String escapedQuery = whitespaceTrimmedQuery; 803 escapedQuery.replace("'", "\\'"); 804 String escapedTagNameQuery = tagNameQuery; 805 escapedTagNameQuery.replace("'", "\\'"); 806 807 // Clear pending jobs. 808 cancelSearch(error); 809 810 // Find all frames, iframes and object elements to search their documents. 811 Vector<Document*> docs = documents(); 812 for (Vector<Document*>::iterator it = docs.begin(); it != docs.end(); ++it) { 813 Document* document = *it; 814 815 if (!tagNameQuery.isEmpty() && startTagFound && endTagFound) { 816 m_pendingMatchJobs.append(new MatchExactTagNamesJob(document, tagNameQuery)); 817 m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery)); 818 continue; 819 } 820 821 if (!tagNameQuery.isEmpty() && startTagFound) { 822 m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[starts-with(name(), '" + escapedTagNameQuery + "')]")); 823 m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery)); 824 continue; 825 } 826 827 if (!tagNameQuery.isEmpty() && endTagFound) { 828 // FIXME: we should have a matchEndOfTagNames search function if endTagFound is true but not startTagFound. 829 // This requires ends-with() support in XPath, WebKit only supports starts-with() and contains(). 830 m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(name(), '" + escapedTagNameQuery + "')]")); 831 m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery)); 832 continue; 833 } 834 835 bool matchesEveryNode = whitespaceTrimmedQuery == "//*" || whitespaceTrimmedQuery == "*"; 836 if (matchesEveryNode) { 837 // These queries will match every node. Matching everything isn't useful and can be slow for large pages, 838 // so limit the search functions list to plain text and attribute matching for these. 839 m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(@*, '" + escapedQuery + "')]")); 840 m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery)); 841 continue; 842 } 843 844 m_pendingMatchJobs.append(new MatchExactIdJob(document, whitespaceTrimmedQuery)); 845 m_pendingMatchJobs.append(new MatchExactClassNamesJob(document, whitespaceTrimmedQuery)); 846 m_pendingMatchJobs.append(new MatchExactTagNamesJob(document, tagNameQuery)); 847 m_pendingMatchJobs.append(new MatchQuerySelectorAllJob(document, "[" + attributeNameQuery + "]")); 848 m_pendingMatchJobs.append(new MatchQuerySelectorAllJob(document, whitespaceTrimmedQuery)); 849 m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(@*, '" + escapedQuery + "')]")); 850 if (!tagNameQuery.isEmpty()) 851 m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(name(), '" + escapedTagNameQuery + "')]")); 852 m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery)); 853 m_pendingMatchJobs.append(new MatchXPathJob(document, whitespaceTrimmedQuery)); 854 } 855 856 if (runSynchronously && *runSynchronously) { 857 // For tests. 858 ListHashSet<Node*> resultCollector; 859 for (Deque<MatchJob*>::iterator it = m_pendingMatchJobs.begin(); it != m_pendingMatchJobs.end(); ++it) 860 (*it)->match(resultCollector); 861 reportNodesAsSearchResults(resultCollector); 862 cancelSearch(error); 863 return; 864 } 865 m_matchJobsTimer.startOneShot(0); 866 } 867 868 void InspectorDOMAgent::cancelSearch(ErrorString*) 869 { 870 if (m_matchJobsTimer.isActive()) 871 m_matchJobsTimer.stop(); 872 deleteAllValues(m_pendingMatchJobs); 873 m_pendingMatchJobs.clear(); 874 m_searchResults.clear(); 875 } 876 877 bool InspectorDOMAgent::handleMousePress() 878 { 879 if (!m_searchingForNode) 880 return false; 881 882 if (m_highlightedNode) { 883 RefPtr<Node> node = m_highlightedNode; 884 setSearchingForNode(false); 885 inspect(node.get()); 886 } 887 return true; 888 } 889 890 void InspectorDOMAgent::inspect(Node* node) 891 { 892 if (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE) 893 node = node->parentNode(); 894 m_nodeToFocus = node; 895 896 focusNode(); 897 } 898 899 void InspectorDOMAgent::focusNode() 900 { 901 if (!m_frontend) 902 return; 903 904 ASSERT(m_nodeToFocus); 905 906 RefPtr<Node> node = m_nodeToFocus.get(); 907 m_nodeToFocus = 0; 908 909 Document* document = node->ownerDocument(); 910 if (!document) 911 return; 912 Frame* frame = document->frame(); 913 if (!frame) 914 return; 915 916 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(mainWorldScriptState(frame)); 917 if (injectedScript.hasNoValue()) 918 return; 919 920 injectedScript.inspectNode(node.get()); 921 } 922 923 void InspectorDOMAgent::mouseDidMoveOverElement(const HitTestResult& result, unsigned) 924 { 925 if (!m_searchingForNode) 926 return; 927 928 Node* node = result.innerNode(); 929 while (node && node->nodeType() == Node::TEXT_NODE) 930 node = node->parentNode(); 931 if (node) { 932 ErrorString error; 933 highlight(&error, node); 934 } 935 } 936 937 void InspectorDOMAgent::setSearchingForNode(bool enabled) 938 { 939 if (m_searchingForNode == enabled) 940 return; 941 m_searchingForNode = enabled; 942 if (!enabled) { 943 ErrorString error; 944 hideHighlight(&error); 945 } 946 } 947 948 void InspectorDOMAgent::setSearchingForNode(ErrorString*, bool enabled) 949 { 950 setSearchingForNode(enabled); 951 } 952 953 void InspectorDOMAgent::highlight(ErrorString*, Node* node) 954 { 955 ASSERT_ARG(node, node); 956 m_highlightedNode = node; 957 m_client->highlight(node); 958 } 959 960 void InspectorDOMAgent::highlightDOMNode(ErrorString* error, int nodeId) 961 { 962 if (Node* node = nodeForId(nodeId)) 963 highlight(error, node); 964 } 965 966 void InspectorDOMAgent::highlightFrame(ErrorString* error, const String& frameId) 967 { 968 Frame* frame = m_instrumentingAgents->inspectorResourceAgent()->frameForId(frameId); 969 if (frame && frame->ownerElement()) 970 highlight(error, frame->ownerElement()); 971 } 972 973 void InspectorDOMAgent::hideHighlight(ErrorString*) 974 { 975 m_highlightedNode = 0; 976 m_client->hideHighlight(); 977 } 978 979 void InspectorDOMAgent::resolveNode(ErrorString* error, int nodeId, RefPtr<InspectorObject>* result) 980 { 981 Node* node = nodeForId(nodeId); 982 if (!node) { 983 *error = "No node with given id found."; 984 return; 985 } 986 *result = resolveNode(node); 987 } 988 989 void InspectorDOMAgent::pushNodeToFrontend(ErrorString*, const String& objectId, int* nodeId) 990 { 991 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId); 992 Node* node = injectedScript.nodeForObjectId(objectId); 993 if (node) 994 *nodeId = pushNodePathToFrontend(node); 995 else 996 *nodeId = 0; 997 } 998 999 String InspectorDOMAgent::documentURLString(Document* document) const 1000 { 1001 if (!document || document->url().isNull()) 1002 return ""; 1003 return document->url().string(); 1004 } 1005 1006 PassRefPtr<InspectorObject> InspectorDOMAgent::buildObjectForNode(Node* node, int depth, NodeToIdMap* nodesMap) 1007 { 1008 RefPtr<InspectorObject> value = InspectorObject::create(); 1009 1010 int id = bind(node, nodesMap); 1011 String nodeName; 1012 String localName; 1013 String nodeValue; 1014 1015 switch (node->nodeType()) { 1016 case Node::TEXT_NODE: 1017 case Node::COMMENT_NODE: 1018 case Node::CDATA_SECTION_NODE: 1019 nodeValue = node->nodeValue(); 1020 break; 1021 case Node::ATTRIBUTE_NODE: 1022 localName = node->localName(); 1023 break; 1024 case Node::DOCUMENT_FRAGMENT_NODE: 1025 break; 1026 case Node::DOCUMENT_NODE: 1027 case Node::ELEMENT_NODE: 1028 default: 1029 nodeName = node->nodeName(); 1030 localName = node->localName(); 1031 break; 1032 } 1033 1034 value->setNumber("id", id); 1035 value->setNumber("nodeType", node->nodeType()); 1036 value->setString("nodeName", nodeName); 1037 value->setString("localName", localName); 1038 value->setString("nodeValue", nodeValue); 1039 1040 if (node->nodeType() == Node::ELEMENT_NODE || node->nodeType() == Node::DOCUMENT_NODE || node->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) { 1041 int nodeCount = innerChildNodeCount(node); 1042 value->setNumber("childNodeCount", nodeCount); 1043 RefPtr<InspectorArray> children = buildArrayForContainerChildren(node, depth, nodesMap); 1044 if (children->length() > 0) 1045 value->setArray("children", children.release()); 1046 1047 if (node->nodeType() == Node::ELEMENT_NODE) { 1048 Element* element = static_cast<Element*>(node); 1049 value->setArray("attributes", buildArrayForElementAttributes(element)); 1050 if (node->isFrameOwnerElement()) { 1051 HTMLFrameOwnerElement* frameOwner = static_cast<HTMLFrameOwnerElement*>(node); 1052 value->setString("documentURL", documentURLString(frameOwner->contentDocument())); 1053 } 1054 } else if (node->nodeType() == Node::DOCUMENT_NODE) { 1055 Document* document = static_cast<Document*>(node); 1056 value->setString("documentURL", documentURLString(document)); 1057 } 1058 } else if (node->nodeType() == Node::DOCUMENT_TYPE_NODE) { 1059 DocumentType* docType = static_cast<DocumentType*>(node); 1060 value->setString("publicId", docType->publicId()); 1061 value->setString("systemId", docType->systemId()); 1062 value->setString("internalSubset", docType->internalSubset()); 1063 } else if (node->nodeType() == Node::ATTRIBUTE_NODE) { 1064 Attr* attribute = static_cast<Attr*>(node); 1065 value->setString("name", attribute->name()); 1066 value->setString("value", attribute->value()); 1067 } 1068 return value.release(); 1069 } 1070 1071 PassRefPtr<InspectorArray> InspectorDOMAgent::buildArrayForElementAttributes(Element* element) 1072 { 1073 RefPtr<InspectorArray> attributesValue = InspectorArray::create(); 1074 // Go through all attributes and serialize them. 1075 const NamedNodeMap* attrMap = element->attributes(true); 1076 if (!attrMap) 1077 return attributesValue.release(); 1078 unsigned numAttrs = attrMap->length(); 1079 for (unsigned i = 0; i < numAttrs; ++i) { 1080 // Add attribute pair 1081 const Attribute *attribute = attrMap->attributeItem(i); 1082 attributesValue->pushString(attribute->name().toString()); 1083 attributesValue->pushString(attribute->value()); 1084 } 1085 return attributesValue.release(); 1086 } 1087 1088 PassRefPtr<InspectorArray> InspectorDOMAgent::buildArrayForContainerChildren(Node* container, int depth, NodeToIdMap* nodesMap) 1089 { 1090 RefPtr<InspectorArray> children = InspectorArray::create(); 1091 Node* child = innerFirstChild(container); 1092 1093 if (depth == 0) { 1094 // Special-case the only text child - pretend that container's children have been requested. 1095 if (child && child->nodeType() == Node::TEXT_NODE && !innerNextSibling(child)) 1096 return buildArrayForContainerChildren(container, 1, nodesMap); 1097 return children.release(); 1098 } 1099 1100 depth--; 1101 m_childrenRequested.add(bind(container, nodesMap)); 1102 1103 while (child) { 1104 children->pushObject(buildObjectForNode(child, depth, nodesMap)); 1105 child = innerNextSibling(child); 1106 } 1107 return children.release(); 1108 } 1109 1110 PassRefPtr<InspectorObject> InspectorDOMAgent::buildObjectForEventListener(const RegisteredEventListener& registeredEventListener, const AtomicString& eventType, Node* node) 1111 { 1112 RefPtr<EventListener> eventListener = registeredEventListener.listener; 1113 RefPtr<InspectorObject> value = InspectorObject::create(); 1114 value->setString("type", eventType); 1115 value->setBoolean("useCapture", registeredEventListener.useCapture); 1116 value->setBoolean("isAttribute", eventListener->isAttribute()); 1117 value->setNumber("nodeId", pushNodePathToFrontend(node)); 1118 value->setString("listenerBody", eventListenerHandlerBody(node->document(), eventListener.get())); 1119 String sourceName; 1120 int lineNumber; 1121 if (eventListenerHandlerLocation(node->document(), eventListener.get(), sourceName, lineNumber)) { 1122 value->setString("sourceName", sourceName); 1123 value->setNumber("lineNumber", lineNumber); 1124 } 1125 return value.release(); 1126 } 1127 1128 Node* InspectorDOMAgent::innerFirstChild(Node* node) 1129 { 1130 if (node->isFrameOwnerElement()) { 1131 HTMLFrameOwnerElement* frameOwner = static_cast<HTMLFrameOwnerElement*>(node); 1132 Document* doc = frameOwner->contentDocument(); 1133 if (doc) 1134 return doc->firstChild(); 1135 } 1136 node = node->firstChild(); 1137 while (isWhitespace(node)) 1138 node = node->nextSibling(); 1139 return node; 1140 } 1141 1142 Node* InspectorDOMAgent::innerNextSibling(Node* node) 1143 { 1144 do { 1145 node = node->nextSibling(); 1146 } while (isWhitespace(node)); 1147 return node; 1148 } 1149 1150 Node* InspectorDOMAgent::innerPreviousSibling(Node* node) 1151 { 1152 do { 1153 node = node->previousSibling(); 1154 } while (isWhitespace(node)); 1155 return node; 1156 } 1157 1158 unsigned InspectorDOMAgent::innerChildNodeCount(Node* node) 1159 { 1160 unsigned count = 0; 1161 Node* child = innerFirstChild(node); 1162 while (child) { 1163 count++; 1164 child = innerNextSibling(child); 1165 } 1166 return count; 1167 } 1168 1169 Node* InspectorDOMAgent::innerParentNode(Node* node) 1170 { 1171 ContainerNode* parent = node->parentNode(); 1172 if (parent && parent->isDocumentNode()) 1173 return static_cast<Document*>(parent)->ownerElement(); 1174 return parent; 1175 } 1176 1177 bool InspectorDOMAgent::isWhitespace(Node* node) 1178 { 1179 //TODO: pull ignoreWhitespace setting from the frontend and use here. 1180 return node && node->nodeType() == Node::TEXT_NODE && node->nodeValue().stripWhiteSpace().length() == 0; 1181 } 1182 1183 void InspectorDOMAgent::mainFrameDOMContentLoaded() 1184 { 1185 // Re-push document once it is loaded. 1186 discardBindings(); 1187 if (m_inspectorState->getBoolean(DOMAgentState::documentRequested)) 1188 m_frontend->documentUpdated(); 1189 } 1190 1191 void InspectorDOMAgent::loadEventFired(Document* document) 1192 { 1193 Element* frameOwner = document->ownerElement(); 1194 if (!frameOwner) 1195 return; 1196 1197 int frameOwnerId = m_documentNodeToIdMap.get(frameOwner); 1198 if (!frameOwnerId) 1199 return; 1200 1201 if (!m_childrenRequested.contains(frameOwnerId)) { 1202 // No children are mapped yet -> only notify on changes of hasChildren. 1203 m_frontend->childNodeCountUpdated(frameOwnerId, innerChildNodeCount(frameOwner)); 1204 } else { 1205 // Re-add frame owner element together with its new children. 1206 int parentId = m_documentNodeToIdMap.get(innerParentNode(frameOwner)); 1207 m_frontend->childNodeRemoved(parentId, frameOwnerId); 1208 RefPtr<InspectorObject> value = buildObjectForNode(frameOwner, 0, &m_documentNodeToIdMap); 1209 Node* previousSibling = innerPreviousSibling(frameOwner); 1210 int prevId = previousSibling ? m_documentNodeToIdMap.get(previousSibling) : 0; 1211 m_frontend->childNodeInserted(parentId, prevId, value.release()); 1212 // Invalidate children requested flag for the element. 1213 m_childrenRequested.remove(m_childrenRequested.find(frameOwnerId)); 1214 } 1215 } 1216 1217 void InspectorDOMAgent::didInsertDOMNode(Node* node) 1218 { 1219 if (isWhitespace(node)) 1220 return; 1221 1222 // We could be attaching existing subtree. Forget the bindings. 1223 unbind(node, &m_documentNodeToIdMap); 1224 1225 ContainerNode* parent = node->parentNode(); 1226 int parentId = m_documentNodeToIdMap.get(parent); 1227 // Return if parent is not mapped yet. 1228 if (!parentId) 1229 return; 1230 1231 if (!m_childrenRequested.contains(parentId)) { 1232 // No children are mapped yet -> only notify on changes of hasChildren. 1233 m_frontend->childNodeCountUpdated(parentId, innerChildNodeCount(parent)); 1234 } else { 1235 // Children have been requested -> return value of a new child. 1236 Node* prevSibling = innerPreviousSibling(node); 1237 int prevId = prevSibling ? m_documentNodeToIdMap.get(prevSibling) : 0; 1238 RefPtr<InspectorObject> value = buildObjectForNode(node, 0, &m_documentNodeToIdMap); 1239 m_frontend->childNodeInserted(parentId, prevId, value.release()); 1240 } 1241 } 1242 1243 void InspectorDOMAgent::didRemoveDOMNode(Node* node) 1244 { 1245 if (isWhitespace(node)) 1246 return; 1247 1248 ContainerNode* parent = node->parentNode(); 1249 int parentId = m_documentNodeToIdMap.get(parent); 1250 // If parent is not mapped yet -> ignore the event. 1251 if (!parentId) 1252 return; 1253 1254 if (m_domListener) 1255 m_domListener->didRemoveDOMNode(node); 1256 1257 if (!m_childrenRequested.contains(parentId)) { 1258 // No children are mapped yet -> only notify on changes of hasChildren. 1259 if (innerChildNodeCount(parent) == 1) 1260 m_frontend->childNodeCountUpdated(parentId, 0); 1261 } else 1262 m_frontend->childNodeRemoved(parentId, m_documentNodeToIdMap.get(node)); 1263 unbind(node, &m_documentNodeToIdMap); 1264 } 1265 1266 void InspectorDOMAgent::didModifyDOMAttr(Element* element) 1267 { 1268 int id = m_documentNodeToIdMap.get(element); 1269 // If node is not mapped yet -> ignore the event. 1270 if (!id) 1271 return; 1272 1273 if (m_domListener) 1274 m_domListener->didModifyDOMAttr(element); 1275 1276 m_frontend->attributesUpdated(id, buildArrayForElementAttributes(element)); 1277 } 1278 1279 void InspectorDOMAgent::characterDataModified(CharacterData* characterData) 1280 { 1281 int id = m_documentNodeToIdMap.get(characterData); 1282 if (!id) 1283 return; 1284 m_frontend->characterDataModified(id, characterData->data()); 1285 } 1286 1287 void InspectorDOMAgent::didInvalidateStyleAttr(Node* node) 1288 { 1289 int id = m_documentNodeToIdMap.get(node); 1290 // If node is not mapped yet -> ignore the event. 1291 if (!id) 1292 return; 1293 1294 if (!m_revalidateStyleAttrTask) 1295 m_revalidateStyleAttrTask = new RevalidateStyleAttributeTask(this); 1296 m_revalidateStyleAttrTask->scheduleFor(static_cast<Element*>(node)); 1297 } 1298 1299 Node* InspectorDOMAgent::nodeForPath(const String& path) 1300 { 1301 // The path is of form "1,HTML,2,BODY,1,DIV" 1302 if (!m_document) 1303 return 0; 1304 1305 Node* node = m_document.get(); 1306 Vector<String> pathTokens; 1307 path.split(",", false, pathTokens); 1308 if (!pathTokens.size()) 1309 return 0; 1310 for (size_t i = 0; i < pathTokens.size() - 1; i += 2) { 1311 bool success = true; 1312 unsigned childNumber = pathTokens[i].toUInt(&success); 1313 if (!success) 1314 return 0; 1315 if (childNumber >= innerChildNodeCount(node)) 1316 return 0; 1317 1318 Node* child = innerFirstChild(node); 1319 String childName = pathTokens[i + 1]; 1320 for (size_t j = 0; child && j < childNumber; ++j) 1321 child = innerNextSibling(child); 1322 1323 if (!child || child->nodeName() != childName) 1324 return 0; 1325 node = child; 1326 } 1327 return node; 1328 } 1329 1330 PassRefPtr<InspectorArray> InspectorDOMAgent::toArray(const Vector<String>& data) 1331 { 1332 RefPtr<InspectorArray> result = InspectorArray::create(); 1333 for (unsigned i = 0; i < data.size(); ++i) 1334 result->pushString(data[i]); 1335 return result.release(); 1336 } 1337 1338 void InspectorDOMAgent::onMatchJobsTimer(Timer<InspectorDOMAgent>*) 1339 { 1340 if (!m_pendingMatchJobs.size()) { 1341 ErrorString error; 1342 cancelSearch(&error); 1343 return; 1344 } 1345 1346 ListHashSet<Node*> resultCollector; 1347 MatchJob* job = m_pendingMatchJobs.takeFirst(); 1348 job->match(resultCollector); 1349 delete job; 1350 1351 reportNodesAsSearchResults(resultCollector); 1352 1353 m_matchJobsTimer.startOneShot(0.025); 1354 } 1355 1356 void InspectorDOMAgent::reportNodesAsSearchResults(ListHashSet<Node*>& resultCollector) 1357 { 1358 RefPtr<InspectorArray> nodeIds = InspectorArray::create(); 1359 for (ListHashSet<Node*>::iterator it = resultCollector.begin(); it != resultCollector.end(); ++it) { 1360 if (m_searchResults.contains(*it)) 1361 continue; 1362 m_searchResults.add(*it); 1363 nodeIds->pushNumber(pushNodePathToFrontend(*it)); 1364 } 1365 m_frontend->searchResults(nodeIds.release()); 1366 } 1367 1368 void InspectorDOMAgent::copyNode(ErrorString*, int nodeId) 1369 { 1370 Node* node = nodeForId(nodeId); 1371 if (!node) 1372 return; 1373 String markup = createMarkup(node); 1374 Pasteboard::generalPasteboard()->writePlainText(markup); 1375 } 1376 1377 void InspectorDOMAgent::pushNodeByPathToFrontend(ErrorString*, const String& path, int* nodeId) 1378 { 1379 if (Node* node = nodeForPath(path)) 1380 *nodeId = pushNodePathToFrontend(node); 1381 } 1382 1383 PassRefPtr<InspectorObject> InspectorDOMAgent::resolveNode(Node* node) 1384 { 1385 Document* document = node->ownerDocument(); 1386 Frame* frame = document ? document->frame() : 0; 1387 if (!frame) 1388 return 0; 1389 1390 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(mainWorldScriptState(frame)); 1391 if (injectedScript.hasNoValue()) 1392 return 0; 1393 1394 return injectedScript.wrapNode(node); 1395 } 1396 1397 void InspectorDOMAgent::drawNodeHighlight(GraphicsContext& context) const 1398 { 1399 if (!m_highlightedNode) 1400 return; 1401 1402 DOMNodeHighlighter::DrawNodeHighlight(context, m_highlightedNode.get()); 1403 } 1404 1405 } // namespace WebCore 1406 1407 #endif // ENABLE(INSPECTOR) 1408