2010-10-14 Darin Adler <darin@apple.com>
[WebKit-https.git] / WebCore / inspector / InspectorDOMAgent.cpp
1 /*
2  * Copyright (C) 2009 Apple Inc. All rights reserved.
3  * Copyright (C) 2009 Google Inc. All rights reserved.
4  * Copyright (C) 2009 Joseph Pecoraro
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1.  Redistributions of source code must retain the above copyright
11  *     notice, this list of conditions and the following disclaimer.
12  * 2.  Redistributions in binary form must reproduce the above copyright
13  *     notice, this list of conditions and the following disclaimer in the
14  *     documentation and/or other materials provided with the distribution.
15  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
16  *     its contributors may be used to endorse or promote products derived
17  *     from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "InspectorDOMAgent.h"
33
34 #if ENABLE(INSPECTOR)
35
36 #include "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 "DOMWindow.h"
51 #include "Document.h"
52 #include "DocumentType.h"
53 #include "Event.h"
54 #include "EventListener.h"
55 #include "EventNames.h"
56 #include "EventTarget.h"
57 #include "Frame.h"
58 #include "FrameTree.h"
59 #include "HTMLElement.h"
60 #include "HTMLFrameOwnerElement.h"
61 #include "InspectorFrontend.h"
62 #include "MutationEvent.h"
63 #include "Node.h"
64 #include "NodeList.h"
65 #include "Pasteboard.h"
66 #include "PlatformString.h"
67 #include "RenderStyle.h"
68 #include "RenderStyleConstants.h"
69 #include "ScriptEventListener.h"
70 #include "StyleSheetList.h"
71 #include "Text.h"
72
73 #if ENABLE(XPATH)
74 #include "XPathResult.h"
75 #endif
76
77 #include "markup.h"
78
79 #include <wtf/text/CString.h>
80 #include <wtf/text/StringConcatenate.h>
81 #include <wtf/HashSet.h>
82 #include <wtf/ListHashSet.h>
83 #include <wtf/OwnPtr.h>
84 #include <wtf/Vector.h>
85 #include <wtf/text/AtomicString.h>
86
87 namespace WebCore {
88
89 class MatchJob {
90 public:
91     virtual void match(ListHashSet<Node*>& resultCollector) = 0;
92     virtual ~MatchJob() { }
93
94 protected:
95     MatchJob(Document* document, const String& query)
96         : m_document(document)
97         , m_query(query) { }
98
99     void addNodesToResults(PassRefPtr<NodeList> nodes, ListHashSet<Node*>& resultCollector)
100     {
101         for (unsigned i = 0; nodes && i < nodes->length(); ++i)
102             resultCollector.add(nodes->item(i));
103     }
104
105     RefPtr<Document> m_document;
106     String m_query;
107 };
108
109 namespace {
110
111 class MatchExactIdJob : public WebCore::MatchJob {
112 public:
113     MatchExactIdJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { }
114     virtual ~MatchExactIdJob() { }
115
116 protected:
117     virtual void match(ListHashSet<Node*>& resultCollector)
118     {
119         if (m_query.isEmpty())
120             return;
121
122         Element* element = m_document->getElementById(m_query);
123         if (element)
124             resultCollector.add(element);
125     }
126 };
127
128 class MatchExactClassNamesJob : public WebCore::MatchJob {
129 public:
130     MatchExactClassNamesJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { }
131     virtual ~MatchExactClassNamesJob() { }
132
133     virtual void match(ListHashSet<Node*>& resultCollector)
134     {
135         if (!m_query.isEmpty())
136             addNodesToResults(m_document->getElementsByClassName(m_query), resultCollector);
137     }
138 };
139
140 class MatchExactTagNamesJob : public WebCore::MatchJob {
141 public:
142     MatchExactTagNamesJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { }
143     virtual ~MatchExactTagNamesJob() { }
144
145     virtual void match(ListHashSet<Node*>& resultCollector)
146     {
147         if (!m_query.isEmpty())
148             addNodesToResults(m_document->getElementsByName(m_query), resultCollector);
149     }
150 };
151
152 class MatchQuerySelectorAllJob : public WebCore::MatchJob {
153 public:
154     MatchQuerySelectorAllJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { }
155     virtual ~MatchQuerySelectorAllJob() { }
156
157     virtual void match(ListHashSet<Node*>& resultCollector)
158     {
159         if (m_query.isEmpty())
160             return;
161
162         ExceptionCode ec = 0;
163         RefPtr<NodeList> list = m_document->querySelectorAll(m_query, ec);
164         if (!ec)
165             addNodesToResults(list, resultCollector);
166     }
167 };
168
169 class MatchXPathJob : public WebCore::MatchJob {
170 public:
171     MatchXPathJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { }
172     virtual ~MatchXPathJob() { }
173
174     virtual void match(ListHashSet<Node*>& resultCollector)
175     {
176 #if ENABLE(XPATH)
177         if (m_query.isEmpty())
178             return;
179
180         ExceptionCode ec = 0;
181         RefPtr<XPathResult> result = m_document->evaluate(m_query, m_document.get(), 0, XPathResult::ORDERED_NODE_SNAPSHOT_TYPE, 0, ec);
182         if (ec || !result)
183             return;
184
185         unsigned long size = result->snapshotLength(ec);
186         for (unsigned long i = 0; !ec && i < size; ++i) {
187             Node* node = result->snapshotItem(i, ec);
188             if (!ec)
189                 resultCollector.add(node);
190         }
191 #else
192         UNUSED_PARAM(resultCollector);
193 #endif
194     }
195 };
196
197 class MatchPlainTextJob : public MatchXPathJob {
198 public:
199     MatchPlainTextJob(Document* document, const String& query) : MatchXPathJob(document, query)
200     {
201         m_query = "//text()[contains(., '" + m_query + "')] | //comment()[contains(., '" + m_query + "')]";
202     }
203     virtual ~MatchPlainTextJob() { }
204 };
205
206 enum DOMBreakpointType {
207     SubtreeModified = 0,
208     AttributeModified,
209     NodeRemoved,
210     DOMBreakpointTypesCount
211 };
212
213 const uint32_t inheritableDOMBreakpointTypesMask = (1 << SubtreeModified);
214 const int domBreakpointDerivedTypeShift = 16;
215
216 }
217
218 InspectorDOMAgent::InspectorDOMAgent(InspectorCSSStore* cssStore, InspectorFrontend* frontend)
219     : EventListener(InspectorDOMAgentType)
220     , m_cssStore(cssStore)
221     , m_frontend(frontend)
222     , m_domListener(0)
223     , m_lastNodeId(1)
224     , m_matchJobsTimer(this, &InspectorDOMAgent::onMatchJobsTimer)
225 {
226 }
227
228 InspectorDOMAgent::~InspectorDOMAgent()
229 {
230     reset();
231 }
232
233 void InspectorDOMAgent::reset()
234 {
235     searchCanceled();
236     discardBindings();
237
238     ListHashSet<RefPtr<Document> > copy = m_documents;
239     for (ListHashSet<RefPtr<Document> >::iterator it = copy.begin(); it != copy.end(); ++it)
240         stopListening((*it).get());
241
242     ASSERT(!m_documents.size());
243 }
244
245 void InspectorDOMAgent::setDOMListener(DOMListener* listener)
246 {
247     m_domListener = listener;
248 }
249
250 void InspectorDOMAgent::setDocument(Document* doc)
251 {
252     if (doc == mainFrameDocument())
253         return;
254
255     reset();
256
257     if (doc) {
258         startListening(doc);
259         if (doc->documentElement())
260             pushDocumentToFrontend();
261     } else
262         m_frontend->setDocument(InspectorValue::null());
263 }
264
265 void InspectorDOMAgent::releaseDanglingNodes()
266 {
267     deleteAllValues(m_danglingNodeToIdMaps);
268     m_danglingNodeToIdMaps.clear();
269 }
270
271 void InspectorDOMAgent::startListening(Document* doc)
272 {
273     if (m_documents.contains(doc))
274         return;
275
276     doc->addEventListener(eventNames().DOMContentLoadedEvent, this, false);
277     doc->addEventListener(eventNames().loadEvent, this, true);
278     m_documents.add(doc);
279 }
280
281 void InspectorDOMAgent::stopListening(Document* doc)
282 {
283     if (!m_documents.contains(doc))
284         return;
285
286     doc->removeEventListener(eventNames().DOMContentLoadedEvent, this, false);
287     doc->removeEventListener(eventNames().loadEvent, this, true);
288     m_documents.remove(doc);
289 }
290
291 void InspectorDOMAgent::handleEvent(ScriptExecutionContext*, Event* event)
292 {
293     AtomicString type = event->type();
294     Node* node = event->target()->toNode();
295
296     if (type == eventNames().DOMContentLoadedEvent) {
297         // Re-push document once it is loaded.
298         discardBindings();
299         pushDocumentToFrontend();
300     } else if (type == eventNames().loadEvent) {
301         long frameOwnerId = m_documentNodeToIdMap.get(node);
302         if (!frameOwnerId)
303             return;
304
305         if (!m_childrenRequested.contains(frameOwnerId)) {
306             // No children are mapped yet -> only notify on changes of hasChildren.
307             m_frontend->childNodeCountUpdated(frameOwnerId, innerChildNodeCount(node));
308         } else {
309             // Re-add frame owner element together with its new children.
310             long parentId = m_documentNodeToIdMap.get(innerParentNode(node));
311             m_frontend->childNodeRemoved(parentId, frameOwnerId);
312             RefPtr<InspectorObject> value = buildObjectForNode(node, 0, &m_documentNodeToIdMap);
313             Node* previousSibling = innerPreviousSibling(node);
314             long prevId = previousSibling ? m_documentNodeToIdMap.get(previousSibling) : 0;
315             m_frontend->childNodeInserted(parentId, prevId, value.release());
316             // Invalidate children requested flag for the element.
317             m_childrenRequested.remove(m_childrenRequested.find(frameOwnerId));
318         }
319     }
320 }
321
322 long InspectorDOMAgent::bind(Node* node, NodeToIdMap* nodesMap)
323 {
324     long id = nodesMap->get(node);
325     if (id)
326         return id;
327     id = m_lastNodeId++;
328     nodesMap->set(node, id);
329     m_idToNode.set(id, node);
330     m_idToNodesMap.set(id, nodesMap);
331     return id;
332 }
333
334 void InspectorDOMAgent::unbind(Node* node, NodeToIdMap* nodesMap)
335 {
336     if (node->isFrameOwnerElement()) {
337         const HTMLFrameOwnerElement* frameOwner = static_cast<const HTMLFrameOwnerElement*>(node);
338         stopListening(frameOwner->contentDocument());
339         if (m_domListener)
340             m_domListener->didRemoveDocument(frameOwner->contentDocument());
341         cssStore()->removeDocument(frameOwner->contentDocument());
342     }
343
344     long id = nodesMap->get(node);
345     if (!id)
346         return;
347     m_idToNode.remove(id);
348     nodesMap->remove(node);
349     bool childrenRequested = m_childrenRequested.contains(id);
350     if (childrenRequested) {
351         // Unbind subtree known to client recursively.
352         m_childrenRequested.remove(id);
353         Node* child = innerFirstChild(node);
354         while (child) {
355             unbind(child, nodesMap);
356             child = innerNextSibling(child);
357         }
358     }
359 }
360
361 bool InspectorDOMAgent::pushDocumentToFrontend()
362 {
363     Document* document = mainFrameDocument();
364     if (!document)
365         return false;
366     if (!m_documentNodeToIdMap.contains(document))
367         m_frontend->setDocument(buildObjectForNode(document, 2, &m_documentNodeToIdMap));
368     return true;
369 }
370
371 void InspectorDOMAgent::pushChildNodesToFrontend(long nodeId)
372 {
373     Node* node = nodeForId(nodeId);
374     if (!node || (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE && node->nodeType() != Node::DOCUMENT_FRAGMENT_NODE))
375         return;
376     if (m_childrenRequested.contains(nodeId))
377         return;
378
379     NodeToIdMap* nodeMap = m_idToNodesMap.get(nodeId);
380     RefPtr<InspectorArray> children = buildArrayForContainerChildren(node, 1, nodeMap);
381     m_childrenRequested.add(nodeId);
382     m_frontend->setChildNodes(nodeId, children.release());
383 }
384
385 long InspectorDOMAgent::inspectedNode(unsigned long num)
386 {
387     if (num < m_inspectedNodes.size())
388         return m_inspectedNodes[num];
389     return 0;
390 }
391
392 void InspectorDOMAgent::discardBindings()
393 {
394     m_documentNodeToIdMap.clear();
395     m_idToNode.clear();
396     releaseDanglingNodes();
397     m_childrenRequested.clear();
398     m_inspectedNodes.clear();
399     m_breakpoints.clear();
400 }
401
402 Node* InspectorDOMAgent::nodeForId(long id)
403 {
404     if (!id)
405         return 0;
406
407     HashMap<long, Node*>::iterator it = m_idToNode.find(id);
408     if (it != m_idToNode.end())
409         return it->second;
410     return 0;
411 }
412
413 void InspectorDOMAgent::getChildNodes(long nodeId)
414 {
415     pushChildNodesToFrontend(nodeId);
416 }
417
418 long InspectorDOMAgent::pushNodePathToFrontend(Node* nodeToPush)
419 {
420     ASSERT(nodeToPush);  // Invalid input
421
422     // If we are sending information to the client that is currently being created. Send root node first.
423     if (!pushDocumentToFrontend())
424         return 0;
425
426     // Return id in case the node is known.
427     long result = m_documentNodeToIdMap.get(nodeToPush);
428     if (result)
429         return result;
430
431     Node* node = nodeToPush;
432     Vector<Node*> path;
433     NodeToIdMap* danglingMap = 0;
434     while (true) {
435         Node* parent = innerParentNode(node);
436         if (!parent) {
437             // Node being pushed is detached -> push subtree root.
438             danglingMap = new NodeToIdMap();
439             m_danglingNodeToIdMaps.append(danglingMap);
440             m_frontend->setDetachedRoot(buildObjectForNode(node, 0, danglingMap));
441             break;
442         } else {
443             path.append(parent);
444             if (m_documentNodeToIdMap.get(parent))
445                 break;
446             else
447                 node = parent;
448         }
449     }
450
451     NodeToIdMap* map = danglingMap ? danglingMap : &m_documentNodeToIdMap;
452     for (int i = path.size() - 1; i >= 0; --i) {
453         long nodeId = map->get(path.at(i));
454         ASSERT(nodeId);
455         pushChildNodesToFrontend(nodeId);
456     }
457     return map->get(nodeToPush);
458 }
459
460 void InspectorDOMAgent::setAttribute(long elementId, const String& name, const String& value, bool* success)
461 {
462     Node* node = nodeForId(elementId);
463     if (node && (node->nodeType() == Node::ELEMENT_NODE)) {
464         Element* element = static_cast<Element*>(node);
465         ExceptionCode ec = 0;
466         element->setAttribute(name, value, ec);
467         *success = !ec;
468     }
469 }
470
471 void InspectorDOMAgent::removeAttribute(long elementId, const String& name, bool* success)
472 {
473     Node* node = nodeForId(elementId);
474     if (node && (node->nodeType() == Node::ELEMENT_NODE)) {
475         Element* element = static_cast<Element*>(node);
476         ExceptionCode ec = 0;
477         element->removeAttribute(name, ec);
478         *success = !ec;
479     }
480 }
481
482 void InspectorDOMAgent::removeNode(long nodeId, long* outNodeId)
483 {
484     Node* node = nodeForId(nodeId);
485     if (!node)
486         return;
487
488     ContainerNode* parentNode = node->parentNode();
489     if (!parentNode)
490         return;
491
492     ExceptionCode ec = 0;
493     parentNode->removeChild(node, ec);
494     if (ec)
495         return;
496
497     *outNodeId = nodeId;
498 }
499
500 void InspectorDOMAgent::changeTagName(long nodeId, const String& tagName, long* newId)
501 {
502     Node* oldNode = nodeForId(nodeId);
503     if (!oldNode || !oldNode->isElementNode())
504         return;
505
506     ExceptionCode ec = 0;
507     RefPtr<Element> newElem = oldNode->document()->createElement(tagName, ec);
508     if (ec)
509         return;
510
511     // Copy over the original node's attributes.
512     Element* oldElem = static_cast<Element*>(oldNode);
513     newElem->copyNonAttributeProperties(oldElem);
514     if (oldElem->attributes())
515         newElem->attributes()->setAttributes(*(oldElem->attributes(true)));
516
517     // Copy over the original node's children.
518     Node* child;
519     while ((child = oldNode->firstChild()))
520         newElem->appendChild(child, ec);
521
522     // Replace the old node with the new node
523     ContainerNode* parent = oldNode->parentNode();
524     parent->insertBefore(newElem, oldNode->nextSibling(), ec);
525     parent->removeChild(oldNode, ec);
526
527     if (ec)
528         return;
529
530     *newId = pushNodePathToFrontend(newElem.get());
531     if (m_childrenRequested.contains(nodeId))
532         pushChildNodesToFrontend(*newId);
533 }
534
535 void InspectorDOMAgent::getOuterHTML(long nodeId, WTF::String* outerHTML)
536 {
537     Node* node = nodeForId(nodeId);
538     if (!node || !node->isHTMLElement())
539         return;
540
541     *outerHTML = static_cast<HTMLElement*>(node)->outerHTML();
542 }
543
544 void InspectorDOMAgent::setOuterHTML(long nodeId, const String& outerHTML, long* newId)
545 {
546     Node* node = nodeForId(nodeId);
547     if (!node || !node->isHTMLElement())
548         return;
549
550     bool requiresTotalUpdate = node->nodeName() == "HTML" || node->nodeName() == "BODY" || node->nodeName() == "HEAD";
551
552     bool childrenRequested = m_childrenRequested.contains(nodeId);
553     Node* previousSibling = node->previousSibling();
554     ContainerNode* parentNode = node->parentNode();
555
556     HTMLElement* htmlElement = static_cast<HTMLElement*>(node);
557     ExceptionCode ec = 0;
558     htmlElement->setOuterHTML(outerHTML, ec);
559     if (ec)
560         return;
561
562     if (requiresTotalUpdate) {
563         Document* document = mainFrameDocument();
564         reset();
565         setDocument(document);
566         *newId = 0;
567         return;
568     }
569
570     Node* newNode = previousSibling ? previousSibling->nextSibling() : parentNode->firstChild();
571     *newId = pushNodePathToFrontend(newNode);
572     if (childrenRequested)
573         pushChildNodesToFrontend(*newId);
574 }
575
576 void InspectorDOMAgent::setTextNodeValue(long nodeId, const String& value, bool* success)
577 {
578     Node* node = nodeForId(nodeId);
579     if (node && (node->nodeType() == Node::TEXT_NODE)) {
580         Text* text_node = static_cast<Text*>(node);
581         ExceptionCode ec = 0;
582         text_node->replaceWholeText(value, ec);
583         *success = !ec;
584     }
585 }
586
587 void InspectorDOMAgent::getEventListenersForNode(long nodeId, long* outNodeId, RefPtr<InspectorArray>* listenersArray)
588 {
589     Node* node = nodeForId(nodeId);
590     *outNodeId = nodeId;
591     EventTargetData* d;
592
593     // Quick break if a null node or no listeners at all
594     if (!node || !(d = node->eventTargetData()))
595         return;
596
597     // Get the list of event types this Node is concerned with
598     Vector<AtomicString> eventTypes;
599     const EventListenerMap& listenerMap = d->eventListenerMap;
600     EventListenerMap::const_iterator end = listenerMap.end();
601     for (EventListenerMap::const_iterator iter = listenerMap.begin(); iter != end; ++iter)
602         eventTypes.append(iter->first);
603
604     // Quick break if no useful listeners
605     size_t eventTypesLength = eventTypes.size();
606     if (!eventTypesLength)
607         return;
608
609     // The Node's Event Ancestors (not including self)
610     Vector<RefPtr<ContainerNode> > ancestors;
611     node->eventAncestors(ancestors);
612
613     // Nodes and their Listeners for the concerned event types (order is top to bottom)
614     Vector<EventListenerInfo> eventInformation;
615     for (size_t i = ancestors.size(); i; --i) {
616         ContainerNode* ancestor = ancestors[i - 1].get();
617         for (size_t j = 0; j < eventTypesLength; ++j) {
618             AtomicString& type = eventTypes[j];
619             if (ancestor->hasEventListeners(type))
620                 eventInformation.append(EventListenerInfo(static_cast<Node*>(ancestor), type, ancestor->getEventListeners(type)));
621         }
622     }
623
624     // Insert the Current Node at the end of that list (last in capturing, first in bubbling)
625     for (size_t i = 0; i < eventTypesLength; ++i) {
626         const AtomicString& type = eventTypes[i];
627         eventInformation.append(EventListenerInfo(node, type, node->getEventListeners(type)));
628     }
629
630     // Get Capturing Listeners (in this order)
631     size_t eventInformationLength = eventInformation.size();
632     for (size_t i = 0; i < eventInformationLength; ++i) {
633         const EventListenerInfo& info = eventInformation[i];
634         const EventListenerVector& vector = info.eventListenerVector;
635         for (size_t j = 0; j < vector.size(); ++j) {
636             const RegisteredEventListener& listener = vector[j];
637             if (listener.useCapture)
638                 (*listenersArray)->pushObject(buildObjectForEventListener(listener, info.eventType, info.node));
639         }
640     }
641
642     // Get Bubbling Listeners (reverse order)
643     for (size_t i = eventInformationLength; i; --i) {
644         const EventListenerInfo& info = eventInformation[i - 1];
645         const EventListenerVector& vector = info.eventListenerVector;
646         for (size_t j = 0; j < vector.size(); ++j) {
647             const RegisteredEventListener& listener = vector[j];
648             if (!listener.useCapture)
649                 (*listenersArray)->pushObject(buildObjectForEventListener(listener, info.eventType, info.node));
650         }
651     }
652 }
653
654 void InspectorDOMAgent::addInspectedNode(long nodeId)
655 {
656     m_inspectedNodes.prepend(nodeId);
657     while (m_inspectedNodes.size() > 5)
658         m_inspectedNodes.removeLast();
659 }
660
661 void InspectorDOMAgent::performSearch(const String& whitespaceTrimmedQuery, bool runSynchronously)
662 {
663     // FIXME: Few things are missing here:
664     // 1) Search works with node granularity - number of matches within node is not calculated.
665     // 2) There is no need to push all search results to the front-end at a time, pushing next / previous result
666     //    is sufficient.
667
668     unsigned queryLength = whitespaceTrimmedQuery.length();
669     bool startTagFound = !whitespaceTrimmedQuery.find('<');
670     bool endTagFound = whitespaceTrimmedQuery.reverseFind('>') + 1 == queryLength;
671
672     String tagNameQuery = whitespaceTrimmedQuery;
673     if (startTagFound || endTagFound)
674         tagNameQuery = tagNameQuery.substring(startTagFound ? 1 : 0, endTagFound ? queryLength - 1 : queryLength);
675     if (!Document::isValidName(tagNameQuery))
676         tagNameQuery = "";
677
678     String attributeNameQuery = whitespaceTrimmedQuery;
679     if (!Document::isValidName(attributeNameQuery))
680         attributeNameQuery = "";
681
682     String escapedQuery = whitespaceTrimmedQuery;
683     escapedQuery.replace("'", "\\'");
684     String escapedTagNameQuery = tagNameQuery;
685     escapedTagNameQuery.replace("'", "\\'");
686
687     // Clear pending jobs.
688     searchCanceled();
689
690     // Find all frames, iframes and object elements to search their documents.
691     for (Frame* frame = mainFrameDocument()->frame(); frame; frame = frame->tree()->traverseNext()) {
692         Document* document = frame->document();
693         if (!document)
694             continue;
695
696         if (!tagNameQuery.isEmpty() && startTagFound && endTagFound) {
697             m_pendingMatchJobs.append(new MatchExactTagNamesJob(document, tagNameQuery));
698             m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery));
699             continue;
700         }
701
702         if (!tagNameQuery.isEmpty() && startTagFound) {
703             m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[starts-with(name(), '" + escapedTagNameQuery + "')]"));
704             m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery));
705             continue;
706         }
707
708         if (!tagNameQuery.isEmpty() && endTagFound) {
709             // FIXME: we should have a matchEndOfTagNames search function if endTagFound is true but not startTagFound.
710             // This requires ends-with() support in XPath, WebKit only supports starts-with() and contains().
711             m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(name(), '" + escapedTagNameQuery + "')]"));
712             m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery));
713             continue;
714         }
715
716         bool matchesEveryNode = whitespaceTrimmedQuery == "//*" || whitespaceTrimmedQuery == "*";
717         if (matchesEveryNode) {
718             // These queries will match every node. Matching everything isn't useful and can be slow for large pages,
719             // so limit the search functions list to plain text and attribute matching for these.
720             m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(@*, '" + escapedQuery + "')]"));
721             m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery));
722             continue;
723         }
724
725         m_pendingMatchJobs.append(new MatchExactIdJob(document, whitespaceTrimmedQuery));
726         m_pendingMatchJobs.append(new MatchExactClassNamesJob(document, whitespaceTrimmedQuery));
727         m_pendingMatchJobs.append(new MatchExactTagNamesJob(document, tagNameQuery));
728         m_pendingMatchJobs.append(new MatchQuerySelectorAllJob(document, "[" + attributeNameQuery + "]"));
729         m_pendingMatchJobs.append(new MatchQuerySelectorAllJob(document, whitespaceTrimmedQuery));
730         m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(@*, '" + escapedQuery + "')]"));
731         if (!tagNameQuery.isEmpty())
732             m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(name(), '" + escapedTagNameQuery + "')]"));
733         m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery));
734         m_pendingMatchJobs.append(new MatchXPathJob(document, whitespaceTrimmedQuery));
735     }
736
737     if (runSynchronously) {
738         // For tests.
739         ListHashSet<Node*> resultCollector;
740         for (Deque<MatchJob*>::iterator it = m_pendingMatchJobs.begin(); it != m_pendingMatchJobs.end(); ++it)
741             (*it)->match(resultCollector);
742         reportNodesAsSearchResults(resultCollector);
743         searchCanceled();
744         return;
745     }
746     m_matchJobsTimer.startOneShot(0);
747 }
748
749 void InspectorDOMAgent::searchCanceled()
750 {
751     if (m_matchJobsTimer.isActive())
752         m_matchJobsTimer.stop();
753     deleteAllValues(m_pendingMatchJobs);
754     m_pendingMatchJobs.clear();
755     m_searchResults.clear();
756 }
757
758 String InspectorDOMAgent::setDOMBreakpoint(long nodeId, long type)
759 {
760     Node* node = nodeForId(nodeId);
761     if (!node)
762         return "";
763
764     String breakpointId = createBreakpointId(nodeId, type);
765     if (m_idToBreakpoint.contains(breakpointId))
766         return "";
767
768     m_idToBreakpoint.set(breakpointId, std::make_pair(nodeId, type));
769
770     uint32_t rootBit = 1 << type;
771     m_breakpoints.set(node, m_breakpoints.get(node) | rootBit);
772     if (rootBit & inheritableDOMBreakpointTypesMask) {
773         for (Node* child = innerFirstChild(node); child; child = innerNextSibling(child))
774             updateSubtreeBreakpoints(child, rootBit, true);
775     }
776
777     return breakpointId;
778 }
779
780 void InspectorDOMAgent::removeDOMBreakpoint(const String& breakpointId)
781 {
782     Breakpoint breakpoint = m_idToBreakpoint.take(breakpointId);
783
784     Node* node = nodeForId(breakpoint.first);
785     if (!node)
786         return;
787
788     uint32_t rootBit = 1 << breakpoint.second;
789     uint32_t mask = m_breakpoints.get(node) & ~rootBit;
790     if (mask)
791         m_breakpoints.set(node, mask);
792     else
793         m_breakpoints.remove(node);
794
795     if ((rootBit & inheritableDOMBreakpointTypesMask) && !(mask & (rootBit << domBreakpointDerivedTypeShift))) {
796         for (Node* child = innerFirstChild(node); child; child = innerNextSibling(child))
797             updateSubtreeBreakpoints(child, rootBit, false);
798     }
799 }
800
801 bool InspectorDOMAgent::shouldBreakOnNodeInsertion(Node*, Node* parent, PassRefPtr<InspectorValue>* details)
802 {
803     if (hasBreakpoint(parent, SubtreeModified)) {
804         *details = descriptionForDOMEvent(parent, SubtreeModified, true);
805         return true;
806     }
807     return false;
808 }
809
810 bool InspectorDOMAgent::shouldBreakOnNodeRemoval(Node* node, PassRefPtr<InspectorValue>* details)
811 {
812     if (hasBreakpoint(node, NodeRemoved)) {
813         *details = descriptionForDOMEvent(node, NodeRemoved, false);
814         return true;
815     }
816     if (hasBreakpoint(innerParentNode(node), SubtreeModified)) {
817         *details = descriptionForDOMEvent(node, SubtreeModified, false);
818         return true;
819     }
820     return false;
821 }
822
823 bool InspectorDOMAgent::shouldBreakOnAttributeModification(Element* element, PassRefPtr<InspectorValue>* details)
824 {
825     if (hasBreakpoint(element, AttributeModified)) {
826         *details = descriptionForDOMEvent(element, AttributeModified, false);
827         return true;
828     }
829     return false;
830 }
831
832 PassRefPtr<InspectorValue> InspectorDOMAgent::descriptionForDOMEvent(Node* target, long breakpointType, bool insertion)
833 {
834     ASSERT(hasBreakpoint(target, breakpointType));
835
836     RefPtr<InspectorObject> description = InspectorObject::create();
837     Node* breakpointOwner = target;
838     if ((1 << breakpointType) & inheritableDOMBreakpointTypesMask) {
839         // For inheritable breakpoint types, target node isn't always the same as the node that owns a breakpoint.
840         // Target node may be unknown to frontend, so we need to push it first.
841         long targetNodeId = pushNodePathToFrontend(target);
842         ASSERT(targetNodeId);
843         description->setNumber("targetNodeId", targetNodeId);
844
845         // Find breakpoint owner node.
846         if (!insertion)
847             breakpointOwner = innerParentNode(target);
848         ASSERT(breakpointOwner);
849         while (!(m_breakpoints.get(breakpointOwner) & (1 << breakpointType))) {
850             breakpointOwner = innerParentNode(breakpointOwner);
851             ASSERT(breakpointOwner);
852         }
853
854         if (breakpointType == SubtreeModified)
855             description->setBoolean("insertion", insertion);
856     }
857
858     long breakpointOwnerNodeId = m_documentNodeToIdMap.get(breakpointOwner);
859     ASSERT(breakpointOwnerNodeId);
860     description->setString("breakpointId", createBreakpointId(breakpointOwnerNodeId, breakpointType));
861
862     return description;
863 }
864
865 String InspectorDOMAgent::documentURLString(Document* document) const
866 {
867     if (!document || document->url().isNull())
868         return "";
869     return document->url().string();
870 }
871
872 PassRefPtr<InspectorObject> InspectorDOMAgent::buildObjectForNode(Node* node, int depth, NodeToIdMap* nodesMap)
873 {
874     RefPtr<InspectorObject> value = InspectorObject::create();
875
876     long id = bind(node, nodesMap);
877     String nodeName;
878     String localName;
879     String nodeValue;
880
881     switch (node->nodeType()) {
882         case Node::TEXT_NODE:
883         case Node::COMMENT_NODE:
884         case Node::CDATA_SECTION_NODE:
885             nodeValue = node->nodeValue();
886             break;
887         case Node::ATTRIBUTE_NODE:
888             localName = node->localName();
889             break;
890         case Node::DOCUMENT_FRAGMENT_NODE:
891             break;
892         case Node::DOCUMENT_NODE:
893         case Node::ELEMENT_NODE:
894         default:
895             nodeName = node->nodeName();
896             localName = node->localName();
897             break;
898     }
899
900     value->setNumber("id", id);
901     value->setNumber("nodeType", node->nodeType());
902     value->setString("nodeName", nodeName);
903     value->setString("localName", localName);
904     value->setString("nodeValue", nodeValue);
905
906     if (node->nodeType() == Node::ELEMENT_NODE || node->nodeType() == Node::DOCUMENT_NODE || node->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) {
907         int nodeCount = innerChildNodeCount(node);
908         value->setNumber("childNodeCount", nodeCount);
909         RefPtr<InspectorArray> children = buildArrayForContainerChildren(node, depth, nodesMap);
910         if (children->length() > 0)
911             value->setArray("children", children.release());
912
913         if (node->nodeType() == Node::ELEMENT_NODE) {
914             Element* element = static_cast<Element*>(node);
915             value->setArray("attributes", buildArrayForElementAttributes(element));
916             if (node->isFrameOwnerElement()) {
917                 HTMLFrameOwnerElement* frameOwner = static_cast<HTMLFrameOwnerElement*>(node);
918                 value->setString("documentURL", documentURLString(frameOwner->contentDocument()));
919             }
920         } else if (node->nodeType() == Node::DOCUMENT_NODE) {
921             Document* document = static_cast<Document*>(node);
922             value->setString("documentURL", documentURLString(document));
923         }
924     } else if (node->nodeType() == Node::DOCUMENT_TYPE_NODE) {
925         DocumentType* docType = static_cast<DocumentType*>(node);
926         value->setString("publicId", docType->publicId());
927         value->setString("systemId", docType->systemId());
928         value->setString("internalSubset", docType->internalSubset());
929     } else if (node->nodeType() == Node::ATTRIBUTE_NODE) {
930         Attr* attribute = static_cast<Attr*>(node);
931         value->setString("name", attribute->name());
932         value->setString("value", attribute->value());
933     }
934     return value.release();
935 }
936
937 PassRefPtr<InspectorArray> InspectorDOMAgent::buildArrayForElementAttributes(Element* element)
938 {
939     RefPtr<InspectorArray> attributesValue = InspectorArray::create();
940     // Go through all attributes and serialize them.
941     const NamedNodeMap* attrMap = element->attributes(true);
942     if (!attrMap)
943         return attributesValue.release();
944     unsigned numAttrs = attrMap->length();
945     for (unsigned i = 0; i < numAttrs; ++i) {
946         // Add attribute pair
947         const Attribute *attribute = attrMap->attributeItem(i);
948         attributesValue->pushString(attribute->name().toString());
949         attributesValue->pushString(attribute->value());
950     }
951     return attributesValue.release();
952 }
953
954 PassRefPtr<InspectorArray> InspectorDOMAgent::buildArrayForContainerChildren(Node* container, int depth, NodeToIdMap* nodesMap)
955 {
956     RefPtr<InspectorArray> children = InspectorArray::create();
957     if (depth == 0) {
958         // Special case the_only text child.
959         if (innerChildNodeCount(container) == 1) {
960             Node *child = innerFirstChild(container);
961             if (child->nodeType() == Node::TEXT_NODE)
962                 children->pushObject(buildObjectForNode(child, 0, nodesMap));
963         }
964         return children.release();
965     } else if (depth > 0) {
966         depth--;
967     }
968
969     for (Node *child = innerFirstChild(container); child; child = innerNextSibling(child))
970         children->pushObject(buildObjectForNode(child, depth, nodesMap));
971     return children.release();
972 }
973
974 PassRefPtr<InspectorObject> InspectorDOMAgent::buildObjectForEventListener(const RegisteredEventListener& registeredEventListener, const AtomicString& eventType, Node* node)
975 {
976     RefPtr<EventListener> eventListener = registeredEventListener.listener;
977     RefPtr<InspectorObject> value = InspectorObject::create();
978     value->setString("type", eventType);
979     value->setBoolean("useCapture", registeredEventListener.useCapture);
980     value->setBoolean("isAttribute", eventListener->isAttribute());
981     value->setNumber("nodeId", pushNodePathToFrontend(node));
982     value->setString("listenerBody", eventListenerHandlerBody(node->document(), eventListener.get()));
983     String sourceName;
984     int lineNumber;
985     if (eventListenerHandlerLocation(node->document(), eventListener.get(), sourceName, lineNumber)) {
986         value->setString("sourceName", sourceName);
987         value->setNumber("lineNumber", lineNumber);
988     }
989     return value.release();
990 }
991
992 Node* InspectorDOMAgent::innerFirstChild(Node* node)
993 {
994     if (node->isFrameOwnerElement()) {
995         HTMLFrameOwnerElement* frameOwner = static_cast<HTMLFrameOwnerElement*>(node);
996         Document* doc = frameOwner->contentDocument();
997         if (doc) {
998             startListening(doc);
999             return doc->firstChild();
1000         }
1001     }
1002     node = node->firstChild();
1003     while (isWhitespace(node))
1004         node = node->nextSibling();
1005     return node;
1006 }
1007
1008 Node* InspectorDOMAgent::innerNextSibling(Node* node)
1009 {
1010     do {
1011         node = node->nextSibling();
1012     } while (isWhitespace(node));
1013     return node;
1014 }
1015
1016 Node* InspectorDOMAgent::innerPreviousSibling(Node* node)
1017 {
1018     do {
1019         node = node->previousSibling();
1020     } while (isWhitespace(node));
1021     return node;
1022 }
1023
1024 unsigned InspectorDOMAgent::innerChildNodeCount(Node* node)
1025 {
1026     unsigned count = 0;
1027     Node* child = innerFirstChild(node);
1028     while (child) {
1029         count++;
1030         child = innerNextSibling(child);
1031     }
1032     return count;
1033 }
1034
1035 Node* InspectorDOMAgent::innerParentNode(Node* node)
1036 {
1037     ContainerNode* parent = node->parentNode();
1038     if (parent && parent->isDocumentNode())
1039         return static_cast<Document*>(parent)->ownerElement();
1040     return parent;
1041 }
1042
1043 bool InspectorDOMAgent::isWhitespace(Node* node)
1044 {
1045     //TODO: pull ignoreWhitespace setting from the frontend and use here.
1046     return node && node->nodeType() == Node::TEXT_NODE && node->nodeValue().stripWhiteSpace().length() == 0;
1047 }
1048
1049 Document* InspectorDOMAgent::mainFrameDocument() const
1050 {
1051     ListHashSet<RefPtr<Document> >::const_iterator it = m_documents.begin();
1052     if (it != m_documents.end())
1053         return it->get();
1054     return 0;
1055 }
1056
1057 bool InspectorDOMAgent::operator==(const EventListener& listener)
1058 {
1059     if (const InspectorDOMAgent* inspectorDOMAgentListener = InspectorDOMAgent::cast(&listener))
1060         return mainFrameDocument() == inspectorDOMAgentListener->mainFrameDocument();
1061     return false;
1062 }
1063
1064 void InspectorDOMAgent::didInsertDOMNode(Node* node)
1065 {
1066     if (isWhitespace(node))
1067         return;
1068
1069     if (m_breakpoints.size()) {
1070         uint32_t mask = m_breakpoints.get(innerParentNode(node));
1071         uint32_t inheritableTypesMask = (mask | (mask >> domBreakpointDerivedTypeShift)) & inheritableDOMBreakpointTypesMask;
1072         if (inheritableTypesMask)
1073             updateSubtreeBreakpoints(node, inheritableTypesMask, true);
1074     }
1075
1076     // We could be attaching existing subtree. Forget the bindings.
1077     unbind(node, &m_documentNodeToIdMap);
1078
1079     ContainerNode* parent = node->parentNode();
1080     long parentId = m_documentNodeToIdMap.get(parent);
1081     // Return if parent is not mapped yet.
1082     if (!parentId)
1083         return;
1084
1085     if (!m_childrenRequested.contains(parentId)) {
1086         // No children are mapped yet -> only notify on changes of hasChildren.
1087         m_frontend->childNodeCountUpdated(parentId, innerChildNodeCount(parent));
1088     } else {
1089         // Children have been requested -> return value of a new child.
1090         Node* prevSibling = innerPreviousSibling(node);
1091         long prevId = prevSibling ? m_documentNodeToIdMap.get(prevSibling) : 0;
1092         RefPtr<InspectorObject> value = buildObjectForNode(node, 0, &m_documentNodeToIdMap);
1093         m_frontend->childNodeInserted(parentId, prevId, value.release());
1094     }
1095 }
1096
1097 void InspectorDOMAgent::didRemoveDOMNode(Node* node)
1098 {
1099     if (isWhitespace(node))
1100         return;
1101
1102     if (m_breakpoints.size()) {
1103         // Remove subtree breakpoints.
1104         removeBreakpointsForNode(node);
1105         Vector<Node*> stack(1, innerFirstChild(node));
1106         do {
1107             Node* node = stack.last();
1108             stack.removeLast();
1109             if (!node)
1110                 continue;
1111             removeBreakpointsForNode(node);
1112             stack.append(innerFirstChild(node));
1113             stack.append(innerNextSibling(node));
1114         } while (!stack.isEmpty());
1115     }
1116
1117     ContainerNode* parent = node->parentNode();
1118     long parentId = m_documentNodeToIdMap.get(parent);
1119     // If parent is not mapped yet -> ignore the event.
1120     if (!parentId)
1121         return;
1122
1123     if (m_domListener)
1124         m_domListener->didRemoveDOMNode(node);
1125
1126     if (!m_childrenRequested.contains(parentId)) {
1127         // No children are mapped yet -> only notify on changes of hasChildren.
1128         if (innerChildNodeCount(parent) == 1)
1129             m_frontend->childNodeCountUpdated(parentId, 0);
1130     } else
1131         m_frontend->childNodeRemoved(parentId, m_documentNodeToIdMap.get(node));
1132     unbind(node, &m_documentNodeToIdMap);
1133 }
1134
1135 void InspectorDOMAgent::didModifyDOMAttr(Element* element)
1136 {
1137     long id = m_documentNodeToIdMap.get(element);
1138     // If node is not mapped yet -> ignore the event.
1139     if (!id)
1140         return;
1141
1142     m_frontend->attributesUpdated(id, buildArrayForElementAttributes(element));
1143 }
1144
1145 void InspectorDOMAgent::characterDataModified(CharacterData* characterData)
1146 {
1147     long id = m_documentNodeToIdMap.get(characterData);
1148     if (!id)
1149         return;
1150     m_frontend->characterDataModified(id, characterData->data());
1151 }
1152
1153 bool InspectorDOMAgent::hasBreakpoint(Node* node, long type)
1154 {
1155     uint32_t rootBit = 1 << type;
1156     uint32_t derivedBit = rootBit << domBreakpointDerivedTypeShift;
1157     return m_breakpoints.get(node) & (rootBit | derivedBit);
1158 }
1159
1160 void InspectorDOMAgent::updateSubtreeBreakpoints(Node* node, uint32_t rootMask, bool set)
1161 {
1162     uint32_t oldMask = m_breakpoints.get(node);
1163     uint32_t derivedMask = rootMask << domBreakpointDerivedTypeShift;
1164     uint32_t newMask = set ? oldMask | derivedMask : oldMask & ~derivedMask;
1165     if (newMask)
1166         m_breakpoints.set(node, newMask);
1167     else
1168         m_breakpoints.remove(node);
1169
1170     uint32_t newRootMask = rootMask & ~newMask;
1171     if (!newRootMask)
1172         return;
1173
1174     for (Node* child = innerFirstChild(node); child; child = innerNextSibling(child))
1175         updateSubtreeBreakpoints(child, newRootMask, set);
1176 }
1177
1178 void InspectorDOMAgent::removeBreakpointsForNode(Node* node)
1179 {
1180     uint32_t mask = m_breakpoints.take(node);
1181     if (!mask)
1182         return;
1183     long nodeId = m_documentNodeToIdMap.get(node);
1184     if (!nodeId)
1185         return;
1186     for (int type = 0; type < DOMBreakpointTypesCount; ++type) {
1187         if (mask && (1 << type))
1188             m_idToBreakpoint.remove(createBreakpointId(nodeId, type));
1189     }
1190 }
1191
1192 String InspectorDOMAgent::createBreakpointId(long nodeId, long type)
1193 {
1194     return makeString("dom:", String::number(nodeId), ':', String::number(type));
1195 }
1196
1197 void InspectorDOMAgent::getStyles(long nodeId, bool authorOnly, RefPtr<InspectorValue>* styles)
1198 {
1199     Node* node = nodeForId(nodeId);
1200     if (!node || node->nodeType() != Node::ELEMENT_NODE)
1201         return;
1202
1203     DOMWindow* defaultView = node->ownerDocument()->defaultView();
1204     if (!defaultView)
1205         return;
1206
1207     Element* element = static_cast<Element*>(node);
1208     RefPtr<CSSComputedStyleDeclaration> computedStyleInfo = computedStyle(node, true); // Support the viewing of :visited information in computed style.
1209
1210     RefPtr<InspectorObject> result = InspectorObject::create();
1211     if (element->style())
1212         result->setObject("inlineStyle", buildObjectForStyle(element->style(), true));
1213     result->setObject("computedStyle", buildObjectForStyle(computedStyleInfo.get(), false));
1214
1215     CSSStyleSelector* selector = element->ownerDocument()->styleSelector();
1216     RefPtr<CSSRuleList> matchedRules = selector->styleRulesForElement(element, authorOnly, true);
1217     result->setArray("matchedCSSRules", buildArrayForCSSRules(node->ownerDocument(), matchedRules.get()));
1218
1219     result->setObject("styleAttributes", buildObjectForAttributeStyles(element));
1220     result->setArray("pseudoElements", buildArrayForPseudoElements(element, authorOnly));
1221
1222     RefPtr<InspectorObject> currentStyle = result;
1223     Element* parentElement = element->parentElement();
1224     while (parentElement) {
1225         RefPtr<InspectorObject> parentStyle = InspectorObject::create();
1226         currentStyle->setObject("parent", parentStyle);
1227         if (parentElement->style() && parentElement->style()->length())
1228             parentStyle->setObject("inlineStyle", buildObjectForStyle(parentElement->style(), true));
1229
1230         CSSStyleSelector* parentSelector = parentElement->ownerDocument()->styleSelector();
1231         RefPtr<CSSRuleList> parentMatchedRules = parentSelector->styleRulesForElement(parentElement, authorOnly, true);
1232         parentStyle->setArray("matchedCSSRules", buildArrayForCSSRules(parentElement->ownerDocument(), parentMatchedRules.get()));
1233
1234         parentElement = parentElement->parentElement();
1235         currentStyle = parentStyle;
1236     }
1237     *styles = result.release();
1238 }
1239
1240 void InspectorDOMAgent::getAllStyles(RefPtr<InspectorArray>* styles)
1241 {
1242     for (ListHashSet<RefPtr<Document> >::iterator it = m_documents.begin(); it != m_documents.end(); ++it) {
1243         StyleSheetList* list = (*it)->styleSheets();
1244         for (unsigned i = 0; i < list->length(); ++i) {
1245             StyleSheet* styleSheet = list->item(i);
1246             if (styleSheet->isCSSStyleSheet())
1247                 (*styles)->pushObject(buildObjectForStyleSheet((*it).get(), static_cast<CSSStyleSheet*>(styleSheet)));
1248         }
1249     }
1250 }
1251
1252 void InspectorDOMAgent::getStyleSheet(long styleSheetId, RefPtr<InspectorObject>* styleSheetObject)
1253 {
1254     CSSStyleSheet* styleSheet = cssStore()->styleSheetForId(styleSheetId);
1255     if (styleSheet && styleSheet->document())
1256         *styleSheetObject = buildObjectForStyleSheet(styleSheet->document(), styleSheet);
1257 }
1258
1259 void InspectorDOMAgent::getStyleSourceData(long styleId, RefPtr<InspectorObject>* dataObject)
1260 {
1261     CSSStyleDeclaration* style = cssStore()->styleForId(styleId);
1262     if (!style)
1263         return;
1264     RefPtr<CSSRuleSourceData> sourceData = CSSRuleSourceData::create();
1265     bool success = cssStore()->getRuleSourceData(style, &sourceData);
1266     if (!success)
1267         return;
1268     RefPtr<InspectorObject> result = InspectorObject::create();
1269
1270     RefPtr<InspectorObject> bodyRange = InspectorObject::create();
1271     result->setObject("bodyRange", bodyRange);
1272     bodyRange->setNumber("start", sourceData->styleSourceData->styleBodyRange.start);
1273     bodyRange->setNumber("end", sourceData->styleSourceData->styleBodyRange.end);
1274
1275     RefPtr<InspectorObject> selectorRange = InspectorObject::create();
1276     result->setObject("selectorRange", selectorRange);
1277     selectorRange->setNumber("start", sourceData->selectorListRange.start);
1278     selectorRange->setNumber("end", sourceData->selectorListRange.end);
1279
1280     RefPtr<InspectorArray> propertyRanges = InspectorArray::create();
1281     result->setArray("propertyData", propertyRanges);
1282     Vector<CSSPropertySourceData>& propertyData = sourceData->styleSourceData->propertyData;
1283     for (Vector<CSSPropertySourceData>::iterator it = propertyData.begin(); it != propertyData.end(); ++it) {
1284         RefPtr<InspectorObject> propertyRange = InspectorObject::create();
1285         propertyRange->setString("name", it->name);
1286         propertyRange->setString("value", it->value);
1287         propertyRange->setBoolean("important", it->important);
1288         propertyRange->setBoolean("parsed", it->parsedOk);
1289         propertyRange->setNumber("start", it->range.start);
1290         propertyRange->setNumber("end", it->range.end);
1291         propertyRanges->pushObject(propertyRange);
1292     }
1293     *dataObject = result.release();
1294 }
1295
1296 void InspectorDOMAgent::getInlineStyle(long nodeId, RefPtr<InspectorValue>* style)
1297 {
1298     Node* node = nodeForId(nodeId);
1299     if (!node || node->nodeType() != Node::ELEMENT_NODE)
1300         return;
1301     *style = buildObjectForStyle(static_cast<Element*>(node)->style(), true);
1302 }
1303
1304 void InspectorDOMAgent::getComputedStyle(long nodeId, RefPtr<InspectorValue>* style)
1305 {
1306     Node* node = nodeForId(nodeId);
1307     if (!node || node->nodeType() != Node::ELEMENT_NODE)
1308         return;
1309
1310     DOMWindow* defaultView = node->ownerDocument()->defaultView();
1311     if (!defaultView)
1312         return;
1313
1314     Element* element = static_cast<Element*>(node);
1315     *style = buildObjectForStyle(defaultView->getComputedStyle(element, "").get(), false);
1316 }
1317
1318 PassRefPtr<InspectorObject> InspectorDOMAgent::buildObjectForAttributeStyles(Element* element)
1319 {
1320     RefPtr<InspectorObject> styleAttributes = InspectorObject::create();
1321     NamedNodeMap* attributes = element->attributes();
1322     for (unsigned i = 0; attributes && i < attributes->length(); ++i) {
1323         Attribute* attribute = attributes->attributeItem(i);
1324         if (attribute->style()) {
1325             String attributeName = attribute->localName();
1326             styleAttributes->setObject(attributeName.utf8().data(), buildObjectForStyle(attribute->style(), true));
1327         }
1328     }
1329     return styleAttributes;
1330 }
1331
1332 PassRefPtr<InspectorArray> InspectorDOMAgent::buildArrayForCSSRules(Document* ownerDocument, CSSRuleList* matchedRules)
1333 {
1334     RefPtr<InspectorArray> matchedCSSRules = InspectorArray::create();
1335     for (unsigned i = 0; matchedRules && i < matchedRules->length(); ++i) {
1336         CSSRule* rule = matchedRules->item(i);
1337         if (rule->type() == CSSRule::STYLE_RULE)
1338             matchedCSSRules->pushObject(buildObjectForRule(ownerDocument, static_cast<CSSStyleRule*>(rule)));
1339     }
1340     return matchedCSSRules.release();
1341 }
1342
1343 PassRefPtr<InspectorArray> InspectorDOMAgent::buildArrayForPseudoElements(Element* element, bool authorOnly)
1344 {
1345     RefPtr<InspectorArray> result = InspectorArray::create();
1346     CSSStyleSelector* selector = element->ownerDocument()->styleSelector();
1347     RefPtr<RenderStyle> renderStyle = element->styleForRenderer();
1348
1349     for (PseudoId pseudoId = FIRST_PUBLIC_PSEUDOID; pseudoId < AFTER_LAST_INTERNAL_PSEUDOID; pseudoId = static_cast<PseudoId>(pseudoId + 1)) {
1350         RefPtr<CSSRuleList> matchedRules = selector->pseudoStyleRulesForElement(element, pseudoId, authorOnly, true);
1351         if (matchedRules && matchedRules->length()) {
1352             RefPtr<InspectorObject> pseudoStyles = InspectorObject::create();
1353             pseudoStyles->setNumber("pseudoId", static_cast<int>(pseudoId));
1354             pseudoStyles->setArray("rules", buildArrayForCSSRules(element->ownerDocument(), matchedRules.get()));
1355             result->pushObject(pseudoStyles.release());
1356         }
1357     }
1358     return result.release();
1359 }
1360
1361 void InspectorDOMAgent::applyStyleText(long styleId, const String& styleText, const String& propertyName, bool* success, RefPtr<InspectorValue>* styleObject, RefPtr<InspectorArray>* changedPropertiesArray)
1362 {
1363     CSSStyleDeclaration* style = cssStore()->styleForId(styleId);
1364     if (!style)
1365         return;
1366
1367     // Remove disabled property entry for property with given name.
1368     DisabledStyleDeclaration* disabledStyle = cssStore()->disabledStyleForId(styleId, false);
1369     if (disabledStyle)
1370         disabledStyle->remove(propertyName);
1371
1372     int styleTextLength = styleText.length();
1373
1374     RefPtr<CSSMutableStyleDeclaration> tempMutableStyle = CSSMutableStyleDeclaration::create();
1375     tempMutableStyle->parseDeclaration(styleText);
1376     CSSStyleDeclaration* tempStyle = static_cast<CSSStyleDeclaration*>(tempMutableStyle.get());
1377
1378     if (tempStyle->length() || !styleTextLength) {
1379         ExceptionCode ec = 0;
1380         // The input was parsable or the user deleted everything, so remove the
1381         // original property from the real style declaration. If this represents
1382         // a shorthand remove all the longhand properties.
1383         if (style->getPropertyShorthand(propertyName).isEmpty()) {
1384             Vector<String> longhandProps = longhandProperties(style, propertyName);
1385             for (unsigned i = 0; !ec && i < longhandProps.size(); ++i)
1386                 style->removeProperty(longhandProps[i], ec);
1387         }
1388         // Explicitly delete properties with no shorthands as well as shorthands themselves.
1389         if (!ec)
1390             style->removeProperty(propertyName, ec);
1391
1392         if (ec)
1393             return;
1394     }
1395
1396     // Notify caller that the property was successfully deleted.
1397     if (!styleTextLength) {
1398         (*changedPropertiesArray)->pushString(propertyName);
1399         *success = true;
1400         return;
1401     }
1402
1403     if (!tempStyle->length())
1404         return;
1405
1406     // Iterate of the properties on the test element's style declaration and
1407     // add them to the real style declaration. We take care to move shorthands.
1408     HashSet<String> foundShorthands;
1409     Vector<String> changedProperties;
1410
1411     for (unsigned i = 0; i < tempStyle->length(); ++i) {
1412         String name = tempStyle->item(i);
1413         String shorthand = tempStyle->getPropertyShorthand(name);
1414
1415         if (!shorthand.isEmpty() && foundShorthands.contains(shorthand))
1416             continue;
1417
1418         String value;
1419         String priority;
1420         if (!shorthand.isEmpty()) {
1421             value = shorthandValue(tempStyle, shorthand);
1422             priority = shorthandPriority(tempStyle, shorthand);
1423             foundShorthands.add(shorthand);
1424             name = shorthand;
1425         } else {
1426             value = tempStyle->getPropertyValue(name);
1427             priority = tempStyle->getPropertyPriority(name);
1428         }
1429
1430         // Set the property on the real style declaration.
1431         ExceptionCode ec = 0;
1432         style->setProperty(name, value, priority, ec);
1433         // Remove disabled property entry for property with this name.
1434         if (disabledStyle)
1435             disabledStyle->remove(name);
1436         changedProperties.append(name);
1437     }
1438     *success = true;
1439     *styleObject = buildObjectForStyle(style, true);
1440     *changedPropertiesArray = toArray(changedProperties);
1441 }
1442
1443 void InspectorDOMAgent::setStyleText(long styleId, const String& cssText, bool* success)
1444 {
1445     CSSStyleDeclaration* style = cssStore()->styleForId(styleId);
1446     if (!style)
1447         return;
1448     ExceptionCode ec = 0;
1449     style->setCssText(cssText, ec);
1450     *success = !ec;
1451 }
1452
1453 void InspectorDOMAgent::setStyleProperty(long styleId, const String& name, const String& value, bool* success)
1454 {
1455     CSSStyleDeclaration* style = cssStore()->styleForId(styleId);
1456     if (!style)
1457         return;
1458
1459     ExceptionCode ec = 0;
1460     style->setProperty(name, value, ec);
1461     *success = !ec;
1462 }
1463
1464 void InspectorDOMAgent::toggleStyleEnabled(long styleId, const String& propertyName, bool disabled, RefPtr<InspectorValue>* styleObject)
1465 {
1466     CSSStyleDeclaration* style = cssStore()->styleForId(styleId);
1467     if (!style)
1468         return;
1469
1470     DisabledStyleDeclaration* disabledStyle = cssStore()->disabledStyleForId(styleId, true);
1471
1472     // TODO: make sure this works with shorthands right.
1473     ExceptionCode ec = 0;
1474     if (disabled) {
1475         disabledStyle->set(propertyName, std::make_pair(style->getPropertyValue(propertyName), style->getPropertyPriority(propertyName)));
1476         if (!ec)
1477             style->removeProperty(propertyName, ec);
1478     } else if (disabledStyle->contains(propertyName)) {
1479         PropertyValueAndPriority valueAndPriority = disabledStyle->get(propertyName);
1480         style->setProperty(propertyName, valueAndPriority.first, valueAndPriority.second, ec);
1481         if (!ec)
1482             disabledStyle->remove(propertyName);
1483     }
1484     if (ec)
1485         return;
1486     *styleObject = buildObjectForStyle(style, true);
1487 }
1488
1489 void InspectorDOMAgent::setRuleSelector(long ruleId, const String& selector, long selectedNodeId, RefPtr<InspectorValue>* ruleObject, bool* selectorAffectsNode)
1490 {
1491     CSSStyleRule* rule = cssStore()->ruleForId(ruleId);
1492     if (!rule)
1493         return;
1494
1495     Node* node = nodeForId(selectedNodeId);
1496
1497     CSSStyleSheet* styleSheet = rule->parentStyleSheet();
1498     ExceptionCode ec = 0;
1499     styleSheet->addRule(selector, rule->style()->cssText(), ec);
1500     if (ec)
1501         return;
1502
1503     CSSStyleRule* newRule = static_cast<CSSStyleRule*>(styleSheet->item(styleSheet->length() - 1));
1504     for (unsigned i = 0; i < styleSheet->length(); ++i) {
1505         if (styleSheet->item(i) == rule) {
1506             styleSheet->deleteRule(i, ec);
1507             break;
1508         }
1509     }
1510
1511     if (ec)
1512         return;
1513
1514     *selectorAffectsNode = ruleAffectsNode(newRule, node);
1515     *ruleObject = buildObjectForRule(node->ownerDocument(), newRule);
1516 }
1517
1518 void InspectorDOMAgent::addRule(const String& selector, long selectedNodeId, RefPtr<InspectorValue>* ruleObject, bool* selectorAffectsNode)
1519 {
1520     Node* node = nodeForId(selectedNodeId);
1521     if (!node)
1522         return;
1523
1524     CSSStyleSheet* styleSheet = cssStore()->inspectorStyleSheet(node->ownerDocument(), true);
1525     if (!styleSheet)
1526         return; // could not add a stylesheet to the ownerDocument
1527
1528     ExceptionCode ec = 0;
1529     styleSheet->addRule(selector, "", ec);
1530     if (ec)
1531         return;
1532
1533     CSSStyleRule* newRule = static_cast<CSSStyleRule*>(styleSheet->item(styleSheet->length() - 1));
1534     *selectorAffectsNode = ruleAffectsNode(newRule, node);
1535     *ruleObject = buildObjectForRule(node->ownerDocument(), newRule);
1536 }
1537
1538 void InspectorDOMAgent::getSupportedCSSProperties(RefPtr<InspectorArray>* cssProperties)
1539 {
1540     RefPtr<InspectorArray> properties = InspectorArray::create();
1541     for (int i = 0; i < numCSSProperties; ++i)
1542         properties->pushString(propertyNameStrings[i]);
1543
1544     *cssProperties = properties.release();
1545 }
1546
1547 PassRefPtr<InspectorObject> InspectorDOMAgent::buildObjectForStyle(CSSStyleDeclaration* style, bool bind)
1548 {
1549     RefPtr<InspectorObject> result = InspectorObject::create();
1550     if (bind) {
1551         long styleId = cssStore()->bindStyle(style);
1552         result->setNumber("id", styleId);
1553         CSSStyleSheet* parentStyleSheet = InspectorCSSStore::getParentStyleSheet(style);
1554         if (parentStyleSheet)
1555             result->setNumber("parentStyleSheetId", cssStore()->bindStyleSheet(parentStyleSheet));
1556
1557         DisabledStyleDeclaration* disabledStyle = cssStore()->disabledStyleForId(styleId, false);
1558         if (disabledStyle)
1559             result->setArray("disabled", buildArrayForDisabledStyleProperties(disabledStyle));
1560     }
1561     result->setString("width", style->getPropertyValue("width"));
1562     result->setString("height", style->getPropertyValue("height"));
1563     populateObjectWithStyleProperties(style, result.get());
1564     return result.release();
1565 }
1566
1567 void InspectorDOMAgent::populateObjectWithStyleProperties(CSSStyleDeclaration* style, InspectorObject* result)
1568 {
1569     RefPtr<InspectorArray> properties = InspectorArray::create();
1570     RefPtr<InspectorObject> shorthandValues = InspectorObject::create();
1571
1572     HashSet<String> foundShorthands;
1573     for (unsigned i = 0; i < style->length(); ++i) {
1574         RefPtr<InspectorObject> property = InspectorObject::create();
1575         String name = style->item(i);
1576         property->setString("name", name);
1577         property->setString("priority", style->getPropertyPriority(name));
1578         property->setBoolean("implicit", style->isPropertyImplicit(name));
1579         String shorthand = style->getPropertyShorthand(name);
1580         property->setString("shorthand", shorthand);
1581         if (!shorthand.isEmpty() && !foundShorthands.contains(shorthand)) {
1582             foundShorthands.add(shorthand);
1583             shorthandValues->setString(shorthand, shorthandValue(style, shorthand));
1584         }
1585         property->setString("value", style->getPropertyValue(name));
1586         properties->pushObject(property.release());
1587     }
1588     result->setArray("properties", properties);
1589     result->setObject("shorthandValues", shorthandValues);
1590 }
1591
1592 PassRefPtr<InspectorArray> InspectorDOMAgent::buildArrayForDisabledStyleProperties(DisabledStyleDeclaration* declaration)
1593 {
1594     RefPtr<InspectorArray> properties = InspectorArray::create();
1595     for (DisabledStyleDeclaration::iterator it = declaration->begin(); it != declaration->end(); ++it) {
1596         RefPtr<InspectorObject> property = InspectorObject::create();
1597         property->setString("name", it->first);
1598         property->setString("value", it->second.first);
1599         property->setString("priority", it->second.second);
1600         properties->pushObject(property.release());
1601     }
1602     return properties.release();
1603 }
1604
1605 PassRefPtr<InspectorObject> InspectorDOMAgent::buildObjectForStyleSheet(Document* ownerDocument, CSSStyleSheet* styleSheet)
1606 {
1607     RefPtr<InspectorObject> result = InspectorObject::create();
1608     long id = cssStore()->bindStyleSheet(styleSheet);
1609     result->setNumber("id", id);
1610     result->setBoolean("disabled", styleSheet->disabled());
1611     result->setString("href", styleSheet->href());
1612     result->setString("title", styleSheet->title());
1613     result->setNumber("documentElementId", m_documentNodeToIdMap.get(styleSheet->document()));
1614     RefPtr<InspectorArray> cssRules = InspectorArray::create();
1615     PassRefPtr<CSSRuleList> cssRuleList = CSSRuleList::create(styleSheet, true);
1616     if (cssRuleList) {
1617         for (unsigned i = 0; i < cssRuleList->length(); ++i) {
1618             CSSRule* rule = cssRuleList->item(i);
1619             if (rule->isStyleRule())
1620                 cssRules->pushObject(buildObjectForRule(ownerDocument, static_cast<CSSStyleRule*>(rule)));
1621         }
1622     }
1623     result->setArray("cssRules", cssRules.release());
1624     return result.release();
1625 }
1626
1627 PassRefPtr<InspectorObject> InspectorDOMAgent::buildObjectForRule(Document* ownerDocument, CSSStyleRule* rule)
1628 {
1629     CSSStyleSheet* parentStyleSheet = rule->parentStyleSheet();
1630
1631     RefPtr<InspectorObject> result = InspectorObject::create();
1632     result->setString("selectorText", rule->selectorText());
1633     result->setString("cssText", rule->cssText());
1634     result->setNumber("sourceLine", rule->sourceLine());
1635     result->setString("documentURL", documentURLString(ownerDocument));
1636     if (parentStyleSheet) {
1637         RefPtr<InspectorObject> parentStyleSheetValue = InspectorObject::create();
1638         parentStyleSheetValue->setString("href", parentStyleSheet->href());
1639         parentStyleSheetValue->setNumber("id", cssStore()->bindStyleSheet(parentStyleSheet));
1640         result->setObject("parentStyleSheet", parentStyleSheetValue.release());
1641     }
1642     bool isUserAgent = parentStyleSheet && !parentStyleSheet->ownerNode() && parentStyleSheet->href().isEmpty();
1643     bool isUser = parentStyleSheet && parentStyleSheet->ownerNode() && parentStyleSheet->ownerNode()->nodeName() == "#document";
1644     result->setBoolean("isUserAgent", isUserAgent);
1645     result->setBoolean("isUser", isUser);
1646     result->setBoolean("isViaInspector", rule->parentStyleSheet() == cssStore()->inspectorStyleSheet(ownerDocument, false));
1647
1648     // Bind editable scripts only.
1649     bool bind = !isUserAgent && !isUser;
1650     result->setObject("style", buildObjectForStyle(rule->style(), bind));
1651
1652     if (bind)
1653         result->setNumber("id", cssStore()->bindRule(rule));
1654     return result.release();
1655 }
1656
1657 Vector<String> InspectorDOMAgent::longhandProperties(CSSStyleDeclaration* style, const String& shorthandProperty)
1658 {
1659     Vector<String> properties;
1660     HashSet<String> foundProperties;
1661
1662     for (unsigned i = 0; i < style->length(); ++i) {
1663         String individualProperty = style->item(i);
1664         if (foundProperties.contains(individualProperty) || style->getPropertyShorthand(individualProperty) != shorthandProperty)
1665             continue;
1666         foundProperties.add(individualProperty);
1667         properties.append(individualProperty);
1668     }
1669
1670     return properties;
1671 }
1672
1673 String InspectorDOMAgent::shorthandValue(CSSStyleDeclaration* style, const String& shorthandProperty)
1674 {
1675     String value = style->getPropertyValue(shorthandProperty);
1676     if (value.isEmpty()) {
1677         // Some shorthands (like border) return a null value, so compute a shorthand value.
1678         // FIXME: remove this when http://bugs.webkit.org/show_bug.cgi?id=15823 is fixed.
1679         for (unsigned i = 0; i < style->length(); ++i) {
1680             String individualProperty = style->item(i);
1681             if (style->getPropertyShorthand(individualProperty) != shorthandProperty)
1682                 continue;
1683             if (style->isPropertyImplicit(individualProperty))
1684                 continue;
1685             String individualValue = style->getPropertyValue(individualProperty);
1686             if (individualValue == "initial")
1687                 continue;
1688             if (value.length())
1689                 value.append(" ");
1690             value.append(individualValue);
1691         }
1692     }
1693     return value;
1694 }
1695
1696 String InspectorDOMAgent::shorthandPriority(CSSStyleDeclaration* style, const String& shorthandProperty)
1697 {
1698     String priority = style->getPropertyPriority(shorthandProperty);
1699     if (priority.isEmpty()) {
1700         for (unsigned i = 0; i < style->length(); ++i) {
1701             String individualProperty = style->item(i);
1702             if (style->getPropertyShorthand(individualProperty) != shorthandProperty)
1703                 continue;
1704             priority = style->getPropertyPriority(individualProperty);
1705             break;
1706         }
1707     }
1708     return priority;
1709 }
1710
1711 bool InspectorDOMAgent::ruleAffectsNode(CSSStyleRule* rule, Node* node)
1712 {
1713     if (!node)
1714         return false;
1715     ExceptionCode ec = 0;
1716     RefPtr<NodeList> nodes = node->ownerDocument()->querySelectorAll(rule->selectorText(), ec);
1717     if (ec)
1718         return false;
1719     for (unsigned i = 0; i < nodes->length(); ++i) {
1720         if (nodes->item(i) == node)
1721             return true;
1722     }
1723     return false;
1724 }
1725
1726 Node* InspectorDOMAgent::nodeForPath(const String& path)
1727 {
1728     // The path is of form "1,HTML,2,BODY,1,DIV"
1729     Node* node = mainFrameDocument();
1730     if (!node)
1731         return 0;
1732
1733     Vector<String> pathTokens;
1734     path.split(",", false, pathTokens);
1735     if (!pathTokens.size())
1736         return 0;
1737     for (size_t i = 0; i < pathTokens.size() - 1; i += 2) {
1738         bool success = true;
1739         unsigned childNumber = pathTokens[i].toUInt(&success);
1740         if (!success)
1741             return 0;
1742         if (childNumber >= innerChildNodeCount(node))
1743             return 0;
1744
1745         Node* child = innerFirstChild(node);
1746         String childName = pathTokens[i + 1];
1747         for (size_t j = 0; child && j < childNumber; ++j)
1748             child = innerNextSibling(child);
1749
1750         if (!child || child->nodeName() != childName)
1751             return 0;
1752         node = child;
1753     }
1754     return node;
1755 }
1756
1757 PassRefPtr<InspectorArray> InspectorDOMAgent::toArray(const Vector<String>& data)
1758 {
1759     RefPtr<InspectorArray> result = InspectorArray::create();
1760     for (unsigned i = 0; i < data.size(); ++i)
1761         result->pushString(data[i]);
1762     return result.release();
1763 }
1764
1765 void InspectorDOMAgent::onMatchJobsTimer(Timer<InspectorDOMAgent>*)
1766 {
1767     if (!m_pendingMatchJobs.size()) {
1768         searchCanceled();
1769         return;
1770     }
1771
1772     ListHashSet<Node*> resultCollector;
1773     MatchJob* job = m_pendingMatchJobs.takeFirst();
1774     job->match(resultCollector);
1775     delete job;
1776
1777     reportNodesAsSearchResults(resultCollector);
1778
1779     m_matchJobsTimer.startOneShot(0.025);
1780 }
1781
1782 void InspectorDOMAgent::reportNodesAsSearchResults(ListHashSet<Node*>& resultCollector)
1783 {
1784     RefPtr<InspectorArray> nodeIds = InspectorArray::create();
1785     for (ListHashSet<Node*>::iterator it = resultCollector.begin(); it != resultCollector.end(); ++it) {
1786         if (m_searchResults.contains(*it))
1787             continue;
1788         m_searchResults.add(*it);
1789         nodeIds->pushNumber(static_cast<long long>(pushNodePathToFrontend(*it)));
1790     }
1791     m_frontend->addNodesToSearchResult(nodeIds.release());
1792 }
1793
1794 void InspectorDOMAgent::copyNode(long nodeId)
1795 {
1796     Node* node = nodeForId(nodeId);
1797     if (!node)
1798         return;
1799     String markup = createMarkup(node);
1800     Pasteboard::generalPasteboard()->writePlainText(markup);
1801 }
1802
1803 void InspectorDOMAgent::pushNodeByPathToFrontend(const String& path, long* nodeId)
1804 {
1805     if (Node* node = nodeForPath(path))
1806         *nodeId = pushNodePathToFrontend(node);
1807 }
1808
1809 } // namespace WebCore
1810
1811 #endif // ENABLE(INSPECTOR)