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