Home | History | Annotate | Download | only in inspector
      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