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