Web Inspector: Move all includes behind ENABLE(INSPECTOR) guards
[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
33 #if ENABLE(INSPECTOR)
34
35 #include "InspectorMemoryAgent.h"
36
37 #include "CharacterData.h"
38 #include "DOMWrapperVisitor.h"
39 #include "Document.h"
40 #include "EventListenerMap.h"
41 #include "Frame.h"
42 #include "InspectorFrontend.h"
43 #include "InspectorState.h"
44 #include "InspectorValues.h"
45 #include "InstrumentingAgents.h"
46 #include "Node.h"
47 #include "Page.h"
48 #include "ScriptProfiler.h"
49 #include "StyledElement.h"
50 #include <wtf/HashSet.h>
51 #include <wtf/text/StringBuilder.h>
52
53 using WebCore::TypeBuilder::Memory::DOMGroup;
54 using WebCore::TypeBuilder::Memory::ListenerCount;
55 using WebCore::TypeBuilder::Memory::NodeCount;
56 using WebCore::TypeBuilder::Memory::StringStatistics;
57
58 namespace WebCore {
59
60 namespace {
61
62 String nodeName(Node* node)
63 {
64     if (node->document()->isXHTMLDocument())
65          return node->nodeName();
66     return node->nodeName().lower();
67 }
68
69 int stringSize(StringImpl* string)
70 {
71     int size = string->length();
72     if (!string->is8Bit())
73         size *= 2;
74     return size + sizeof(*string);
75 }
76
77 typedef HashSet<StringImpl*, PtrHash<StringImpl*> > StringImplIdentitySet;
78
79 class CharacterDataStatistics {
80     WTF_MAKE_NONCOPYABLE(CharacterDataStatistics);
81 public:
82     CharacterDataStatistics() : m_characterDataSize(0) { }
83
84     void collectCharacterData(Node* node)
85     {
86         if (!node->isCharacterDataNode())
87             return;
88
89         CharacterData* characterData = static_cast<CharacterData*>(node);
90         StringImpl* dataImpl = characterData->dataImpl();
91         if (m_domStringImplSet.contains(dataImpl))
92             return;
93         m_domStringImplSet.add(dataImpl);
94
95         m_characterDataSize += stringSize(dataImpl);
96     }
97
98     bool contains(StringImpl* s) { return m_domStringImplSet.contains(s); }
99
100     int characterDataSize() { return m_characterDataSize; }
101
102 private:
103     StringImplIdentitySet m_domStringImplSet;
104     int m_characterDataSize;
105 };
106
107 class DOMTreeStatistics {
108     WTF_MAKE_NONCOPYABLE(DOMTreeStatistics);
109 public:
110     DOMTreeStatistics(Node* rootNode, CharacterDataStatistics& characterDataStatistics)
111         : m_totalNodeCount(0)
112         , m_characterDataStatistics(characterDataStatistics)
113     {
114         collectTreeStatistics(rootNode);
115     }
116
117     int totalNodeCount() { return m_totalNodeCount; }
118
119     PassRefPtr<InspectorArray> nodeCount()
120     {
121         RefPtr<InspectorArray> childrenStats = InspectorArray::create();
122         for (HashMap<String, int>::iterator it = m_nodeNameToCount.begin(); it != m_nodeNameToCount.end(); ++it) {
123             RefPtr<NodeCount> nodeCount = NodeCount::create().setNodeName(it->first)
124                                                              .setCount(it->second);
125             childrenStats->pushObject(nodeCount);
126         }
127         return childrenStats.release();
128     }
129
130     PassRefPtr<InspectorArray> listenerCount()
131     {
132         RefPtr<InspectorArray> listenerStats = InspectorArray::create();
133         for (HashMap<AtomicString, int>::iterator it = m_eventTypeToCount.begin(); it != m_eventTypeToCount.end(); ++it) {
134             RefPtr<ListenerCount> listenerCount = ListenerCount::create().setType(it->first)
135                                                                          .setCount(it->second);
136             listenerStats->pushObject(listenerCount);
137         }
138         return listenerStats.release();
139     }
140
141 private:
142     void collectTreeStatistics(Node* rootNode)
143     {
144         Node* currentNode = rootNode;
145         collectListenersInfo(rootNode);
146         while ((currentNode = currentNode->traverseNextNode(rootNode))) {
147             ++m_totalNodeCount;
148             collectNodeStatistics(currentNode);
149         }
150     }
151     void collectNodeStatistics(Node* node)
152     {
153         m_characterDataStatistics.collectCharacterData(node);
154         collectNodeNameInfo(node);
155         collectListenersInfo(node);
156     }
157
158     void collectCharacterData(Node*)
159     {
160     }
161
162     void collectNodeNameInfo(Node* node)
163     {
164         String name = nodeName(node);
165         int currentCount = m_nodeNameToCount.get(name);
166         m_nodeNameToCount.set(name, currentCount + 1);
167     }
168
169     void collectListenersInfo(Node* node)
170     {
171         EventTargetData* d = node->eventTargetData();
172         if (!d)
173             return;
174         EventListenerMap& eventListenerMap = d->eventListenerMap;
175         if (eventListenerMap.isEmpty())
176             return;
177         Vector<AtomicString> eventNames = eventListenerMap.eventTypes();
178         for (Vector<AtomicString>::iterator it = eventNames.begin(); it != eventNames.end(); ++it) {
179             AtomicString name = *it;
180             EventListenerVector* listeners = eventListenerMap.find(name);
181             int count = 0;
182             for (EventListenerVector::iterator j = listeners->begin(); j != listeners->end(); ++j) {
183                 if (j->listener->type() == EventListener::JSEventListenerType)
184                     ++count;
185             }
186             if (count)
187                 m_eventTypeToCount.set(name, m_eventTypeToCount.get(name) + count);
188         }
189     }
190
191     int m_totalNodeCount;
192     HashMap<AtomicString, int> m_eventTypeToCount;
193     HashMap<String, int> m_nodeNameToCount;
194     CharacterDataStatistics& m_characterDataStatistics;
195 };
196
197 class CounterVisitor : public DOMWrapperVisitor {
198 public:
199     CounterVisitor(Page* page)
200         : m_page(page)
201         , m_domGroups(InspectorArray::create())
202         , m_jsExternalStringSize(0)
203         , m_sharedStringSize(0) { }
204
205     InspectorArray* domGroups() { return m_domGroups.get(); }
206
207     PassRefPtr<InspectorObject> strings()
208     {
209         RefPtr<StringStatistics> stringStatistics = StringStatistics::create()
210             .setDom(m_characterDataStatistics.characterDataSize())
211             .setJs(m_jsExternalStringSize)
212             .setShared(m_sharedStringSize);
213         return stringStatistics.release();
214     }
215
216     virtual void visitNode(Node* node)
217     {
218         if (node->document()->frame() && m_page != node->document()->frame()->page())
219             return;
220
221         Node* rootNode = node;
222         while (rootNode->parentNode())
223             rootNode = rootNode->parentNode();
224
225         if (m_roots.contains(rootNode))
226             return;
227         m_roots.add(rootNode);
228
229         DOMTreeStatistics domTreeStats(rootNode, m_characterDataStatistics);
230
231         RefPtr<DOMGroup> domGroup = DOMGroup::create()
232             .setSize(domTreeStats.totalNodeCount())
233             .setTitle(rootNode->nodeType() == Node::ELEMENT_NODE ? elementTitle(static_cast<Element*>(rootNode)) : rootNode->nodeName())
234             .setNodeCount(domTreeStats.nodeCount())
235             .setListenerCount(domTreeStats.listenerCount());
236         if (rootNode->nodeType() == Node::DOCUMENT_NODE)
237             domGroup->setDocumentURI(static_cast<Document*>(rootNode)->documentURI());
238
239         m_domGroups->pushObject(domGroup);
240     }
241
242     virtual void visitJSExternalString(StringImpl* string)
243     {
244         int size = stringSize(string);
245         m_jsExternalStringSize += size;
246         if (m_characterDataStatistics.contains(string))
247             m_sharedStringSize += size;
248     }
249
250 private:
251     String elementTitle(Element* element)
252     {
253         StringBuilder result;
254         result.append(nodeName(element));
255
256         const AtomicString& idValue = element->getIdAttribute();
257         String idString;
258         if (!idValue.isNull() && !idValue.isEmpty()) {
259             result.append("#");
260             result.append(idValue);
261         }
262
263         HashSet<AtomicString> usedClassNames;
264         if (element->hasClass() && element->isStyledElement()) {
265             const SpaceSplitString& classNamesString = static_cast<StyledElement*>(element)->classNames();
266             size_t classNameCount = classNamesString.size();
267             for (size_t i = 0; i < classNameCount; ++i) {
268                 const AtomicString& className = classNamesString[i];
269                 if (usedClassNames.contains(className))
270                     continue;
271                 usedClassNames.add(className);
272                 result.append(".");
273                 result.append(className);
274             }
275         }
276         return result.toString();
277     }
278
279     HashSet<Node*> m_roots;
280     Page* m_page;
281     RefPtr<InspectorArray> m_domGroups;
282     CharacterDataStatistics m_characterDataStatistics;
283     int m_jsExternalStringSize;
284     int m_sharedStringSize;
285 };
286
287 } // namespace
288
289 InspectorMemoryAgent::~InspectorMemoryAgent()
290 {
291 }
292
293 void InspectorMemoryAgent::getDOMNodeCount(ErrorString*, RefPtr<InspectorArray>& domGroups, RefPtr<InspectorObject>& strings)
294 {
295     CounterVisitor counterVisitor(m_page);
296     ScriptProfiler::visitJSDOMWrappers(&counterVisitor);
297
298     // Make sure all documents reachable from the main frame are accounted.
299     for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
300         if (Document* doc = frame->document())
301             counterVisitor.visitNode(doc);
302     }
303
304     ScriptProfiler::visitExternalJSStrings(&counterVisitor);
305
306     domGroups = counterVisitor.domGroups();
307     strings = counterVisitor.strings();
308 }
309
310 InspectorMemoryAgent::InspectorMemoryAgent(InstrumentingAgents* instrumentingAgents, InspectorState* state, Page* page, InspectorDOMAgent* domAgent)
311     : InspectorBaseAgent<InspectorMemoryAgent>("Memory", instrumentingAgents, state)
312     , m_page(page)
313     , m_domAgent(domAgent)
314 {
315 }
316
317 } // namespace WebCore
318
319 #endif // ENABLE(INSPECTOR)