Web Inspector: provide per Document Node count statistics
[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 "Frame.h"
39 #include "InspectorState.h"
40 #include "InspectorValues.h"
41 #include "InstrumentingAgents.h"
42 #include "Node.h"
43 #include "Page.h"
44 #include "ScriptProfiler.h"
45 #include "StyledElement.h"
46 #include <wtf/HashSet.h>
47 #include <wtf/text/StringBuilder.h>
48
49 namespace WebCore {
50
51 namespace {
52
53 class CounterVisitor : public DOMWrapperVisitor {
54 public:
55     CounterVisitor(Page* page) : m_page(page), m_counters(InspectorArray::create()) { }
56
57     InspectorArray* counters() { return m_counters.get(); }
58
59     virtual void visitNode(Node* node)
60     {
61         if (node->document()->frame() && m_page != node->document()->frame()->page())
62             return;
63
64         Node* rootNode = node;
65         while (rootNode->parentNode())
66             rootNode = rootNode->parentNode();
67
68         if (m_roots.contains(rootNode))
69             return;
70         m_roots.add(rootNode);
71
72         RefPtr<InspectorObject> entry = InspectorObject::create();
73         entry->setString("title", rootNode->nodeType() == Node::ELEMENT_NODE ? elementTitle(static_cast<Element*>(rootNode)) : rootNode->nodeName());
74         if (rootNode->nodeType() == Node::DOCUMENT_NODE)
75             entry->setString("documentURI", static_cast<Document*>(rootNode)->documentURI());
76         collectTreeStatistics(rootNode, entry.get());
77         m_counters->pushObject(entry);
78     }
79
80 private:
81     static void collectTreeStatistics(Node* rootNode, InspectorObject* result)
82     {
83         unsigned count = 0;
84         HashMap<String, int> nameToCount;
85         Node* currentNode = rootNode;
86         while ((currentNode = currentNode->traverseNextNode(rootNode))) {
87             ++count;
88             String name = nodeName(currentNode);
89             int currentCount = nameToCount.get(name);
90             nameToCount.set(name, currentCount + 1);
91         }
92
93         RefPtr<InspectorArray> childrenStats = InspectorArray::create();
94         for (HashMap<String, int>::iterator it = nameToCount.begin(); it != nameToCount.end(); ++it) {
95             RefPtr<InspectorObject> nodeCount = InspectorObject::create();
96             nodeCount->setString("nodeName", it->first);
97             nodeCount->setNumber("count", it->second);
98             childrenStats->pushObject(nodeCount);
99         }
100
101         result->setNumber("size", count);
102         result->setArray("nodeCount", childrenStats);
103     }
104
105     static String nodeName(Node* node)
106     {
107         if (node->document()->isXHTMLDocument())
108              return node->nodeName();
109         return node->nodeName().lower();
110     }
111
112     String elementTitle(Element* element)
113     {
114         StringBuilder result;
115         result.append(nodeName(element));
116
117         const AtomicString& idValue = element->getIdAttribute();
118         String idString;
119         if (!idValue.isNull() && !idValue.isEmpty()) {
120             result.append("#");
121             result.append(idValue);
122         }
123
124         HashSet<AtomicString> usedClassNames;
125         if (element->hasClass() && element->isStyledElement()) {
126             const SpaceSplitString& classNamesString = static_cast<StyledElement*>(element)->classNames();
127             size_t classNameCount = classNamesString.size();
128             for (size_t i = 0; i < classNameCount; ++i) {
129                 const AtomicString& className = classNamesString[i];
130                 if (usedClassNames.contains(className))
131                     continue;
132                 usedClassNames.add(className);
133                 result.append(".");
134                 result.append(className);
135             }
136         }
137         return result.toString();
138     }
139
140     HashSet<Node*> m_roots;
141     Page* m_page;
142     RefPtr<InspectorArray> m_counters;
143 };
144
145 } // namespace
146
147 InspectorMemoryAgent::~InspectorMemoryAgent()
148 {
149 }
150
151 void InspectorMemoryAgent::getDOMNodeCount(ErrorString*, RefPtr<InspectorArray>* result)
152 {
153     CounterVisitor counterVisitor(m_page);
154     ScriptProfiler::visitJSDOMWrappers(&counterVisitor);
155
156     // Make sure all documents reachable from the main frame are accounted.
157     for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
158         if (Document* doc = frame->document())
159             counterVisitor.visitNode(doc);
160     }
161
162     *result = counterVisitor.counters();
163 }
164
165 InspectorMemoryAgent::InspectorMemoryAgent(InstrumentingAgents* instrumentingAgents, InspectorState* state, Page* page, InspectorDOMAgent* domAgent)
166     : InspectorBaseAgent<InspectorMemoryAgent>("Memory", instrumentingAgents, state)
167     , m_page(page)
168     , m_domAgent(domAgent)
169 {
170 }
171
172 } // namespace WebCore
173
174 #endif // ENABLE(INSPECTOR)