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