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