Web Inspector: use typed front-end API in the memory agent
[WebKit-https.git] / Source / WebCore / inspector / InspectorMemoryAgent.cpp
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "InspectorMemoryAgent.h"
33
34 #if ENABLE(INSPECTOR)
35
36 #include "DOMWrapperVisitor.h"
37 #include "Document.h"
38 #include "EventListenerMap.h"
39 #include "Frame.h"
40 #include "InspectorFrontend.h"
41 #include "InspectorState.h"
42 #include "InspectorValues.h"
43 #include "InstrumentingAgents.h"
44 #include "Node.h"
45 #include "Page.h"
46 #include "ScriptProfiler.h"
47 #include "StyledElement.h"
48 #include <wtf/HashSet.h>
49 #include <wtf/text/StringBuilder.h>
50
51 using WebCore::TypeBuilder::Memory::DOMGroup;
52 using WebCore::TypeBuilder::Memory::ListenerCount;
53 using WebCore::TypeBuilder::Memory::NodeCount;
54
55 namespace WebCore {
56
57 namespace {
58
59 String nodeName(Node* node)
60 {
61     if (node->document()->isXHTMLDocument())
62          return node->nodeName();
63     return node->nodeName().lower();
64 }
65
66 typedef HashSet<StringImpl*, PtrHash<StringImpl*> > StringImplIdentitySet;
67
68 class DOMTreeStatistics {
69 public:
70     DOMTreeStatistics(Node* rootNode) : m_totalNodeCount(0)
71     {
72         collectTreeStatistics(rootNode);
73     }
74
75     int totalNodeCount() { return m_totalNodeCount; }
76
77     PassRefPtr<InspectorArray> nodeCount()
78     {
79         RefPtr<InspectorArray> childrenStats = InspectorArray::create();
80         for (HashMap<String, int>::iterator it = m_nodeNameToCount.begin(); it != m_nodeNameToCount.end(); ++it) {
81             RefPtr<NodeCount> nodeCount = NodeCount::create().setNodeName(it->first)
82                                                              .setCount(it->second);
83             childrenStats->pushObject(nodeCount);
84         }
85         return childrenStats.release();
86     }
87
88     PassRefPtr<InspectorArray> listenerCount()
89     {
90         RefPtr<InspectorArray> listenerStats = InspectorArray::create();
91         for (HashMap<AtomicString, int>::iterator it = m_eventTypeToCount.begin(); it != m_eventTypeToCount.end(); ++it) {
92             RefPtr<ListenerCount> listenerCount = ListenerCount::create().setType(it->first)
93                                                                          .setCount(it->second);
94             listenerStats->pushObject(listenerCount);
95         }
96         return listenerStats.release();
97     }
98
99 private:
100     void collectTreeStatistics(Node* rootNode)
101     {
102         Node* currentNode = rootNode;
103         collectListenersInfo(rootNode);
104         while ((currentNode = currentNode->traverseNextNode(rootNode))) {
105             ++m_totalNodeCount;
106             collectNodeStatistics(currentNode);
107         }
108     }
109     void collectNodeStatistics(Node* node)
110     {
111         collectCharacterData(node);
112         collectNodeNameInfo(node);
113         collectListenersInfo(node);
114     }
115     
116     void collectCharacterData(Node* node)
117     {
118     }
119
120     void collectNodeNameInfo(Node* node)
121     {
122         String name = nodeName(node);
123         int currentCount = m_nodeNameToCount.get(name);
124         m_nodeNameToCount.set(name, currentCount + 1);
125     }
126
127     void collectListenersInfo(Node* node)
128     {
129         EventTargetData* d = node->eventTargetData();
130         if (!d)
131             return;
132         EventListenerMap& eventListenerMap = d->eventListenerMap;
133         if (eventListenerMap.isEmpty())
134             return;
135         Vector<AtomicString> eventNames = eventListenerMap.eventTypes();
136         for (Vector<AtomicString>::iterator it = eventNames.begin(); it != eventNames.end(); ++it) {
137             AtomicString name = *it;
138             EventListenerVector* listeners = eventListenerMap.find(name);
139             int count = 0;
140             for (EventListenerVector::iterator j = listeners->begin(); j != listeners->end(); ++j) {
141                 if (j->listener->type() == EventListener::JSEventListenerType)
142                     ++count;
143             }
144             if (count)
145                 m_eventTypeToCount.set(name, m_eventTypeToCount.get(name) + count);
146         }
147     }
148
149     int m_totalNodeCount;
150     HashMap<AtomicString, int> m_eventTypeToCount;
151     HashMap<String, int> m_nodeNameToCount;
152     StringImplIdentitySet m_domStringImplSet;
153 };
154
155 class CounterVisitor : public DOMWrapperVisitor {
156 public:
157     CounterVisitor(Page* page) : m_page(page), m_counters(InspectorArray::create()) { }
158
159     InspectorArray* counters() { return m_counters.get(); }
160
161     virtual void visitNode(Node* node)
162     {
163         if (node->document()->frame() && m_page != node->document()->frame()->page())
164             return;
165
166         Node* rootNode = node;
167         while (rootNode->parentNode())
168             rootNode = rootNode->parentNode();
169
170         if (m_roots.contains(rootNode))
171             return;
172         m_roots.add(rootNode);
173
174         DOMTreeStatistics domTreeStats(rootNode);
175
176         RefPtr<DOMGroup> domGroup = DOMGroup::create()
177             .setSize(domTreeStats.totalNodeCount())
178             .setTitle(rootNode->nodeType() == Node::ELEMENT_NODE ? elementTitle(static_cast<Element*>(rootNode)) : rootNode->nodeName())
179             .setNodeCount(domTreeStats.nodeCount())
180             .setListenerCount(domTreeStats.listenerCount());
181         if (rootNode->nodeType() == Node::DOCUMENT_NODE)
182             domGroup->setDocumentURI(static_cast<Document*>(rootNode)->documentURI());
183
184         m_counters->pushObject(domGroup);
185     }
186
187 private:
188     String elementTitle(Element* element)
189     {
190         StringBuilder result;
191         result.append(nodeName(element));
192
193         const AtomicString& idValue = element->getIdAttribute();
194         String idString;
195         if (!idValue.isNull() && !idValue.isEmpty()) {
196             result.append("#");
197             result.append(idValue);
198         }
199
200         HashSet<AtomicString> usedClassNames;
201         if (element->hasClass() && element->isStyledElement()) {
202             const SpaceSplitString& classNamesString = static_cast<StyledElement*>(element)->classNames();
203             size_t classNameCount = classNamesString.size();
204             for (size_t i = 0; i < classNameCount; ++i) {
205                 const AtomicString& className = classNamesString[i];
206                 if (usedClassNames.contains(className))
207                     continue;
208                 usedClassNames.add(className);
209                 result.append(".");
210                 result.append(className);
211             }
212         }
213         return result.toString();
214     }
215
216     HashSet<Node*> m_roots;
217     Page* m_page;
218     RefPtr<InspectorArray> m_counters;
219 };
220
221 } // namespace
222
223 InspectorMemoryAgent::~InspectorMemoryAgent()
224 {
225 }
226
227 void InspectorMemoryAgent::getDOMNodeCount(ErrorString*, RefPtr<InspectorArray>& result)
228 {
229     CounterVisitor counterVisitor(m_page);
230     ScriptProfiler::visitJSDOMWrappers(&counterVisitor);
231
232     // Make sure all documents reachable from the main frame are accounted.
233     for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
234         if (Document* doc = frame->document())
235             counterVisitor.visitNode(doc);
236     }
237
238     result = counterVisitor.counters();
239 }
240
241 InspectorMemoryAgent::InspectorMemoryAgent(InstrumentingAgents* instrumentingAgents, InspectorState* state, Page* page, InspectorDOMAgent* domAgent)
242     : InspectorBaseAgent<InspectorMemoryAgent>("Memory", instrumentingAgents, state)
243     , m_page(page)
244     , m_domAgent(domAgent)
245 {
246 }
247
248 } // namespace WebCore
249
250 #endif // ENABLE(INSPECTOR)