Replace more instances of += with StringBuilder
[WebKit-https.git] / Source / WebCore / inspector / InspectorConsoleAgent.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
6  * are met:
7  * 1.  Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  * 2.  Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25
26 #include "config.h"
27
28 #if ENABLE(INSPECTOR)
29 #include "InspectorConsoleAgent.h"
30
31 #include "InstrumentingAgents.h"
32 #include "Console.h"
33 #include "ConsoleMessage.h"
34 #include "DOMWindow.h"
35 #include "IdentifiersFactory.h"
36 #include "InjectedScriptHost.h"
37 #include "InjectedScriptManager.h"
38 #include "InspectorFrontend.h"
39 #include "InspectorState.h"
40 #include "ResourceError.h"
41 #include "ResourceResponse.h"
42 #include "ScriptArguments.h"
43 #include "ScriptCallFrame.h"
44 #include "ScriptCallStack.h"
45 #include "ScriptObject.h"
46 #include "ScriptProfiler.h"
47 #include <wtf/CurrentTime.h>
48 #include <wtf/OwnPtr.h>
49 #include <wtf/PassOwnPtr.h>
50 #include <wtf/text/StringBuilder.h>
51 #include <wtf/text/WTFString.h>
52
53 namespace WebCore {
54
55 static const unsigned maximumConsoleMessages = 1000;
56 static const int expireConsoleMessagesStep = 100;
57
58 namespace ConsoleAgentState {
59 static const char monitoringXHR[] = "monitoringXHR";
60 static const char consoleMessagesEnabled[] = "consoleMessagesEnabled";
61 }
62
63 InspectorConsoleAgent::InspectorConsoleAgent(InstrumentingAgents* instrumentingAgents, InspectorState* state, InjectedScriptManager* injectedScriptManager)
64     : InspectorBaseAgent<InspectorConsoleAgent>("Console", instrumentingAgents, state)
65     , m_injectedScriptManager(injectedScriptManager)
66     , m_frontend(0)
67     , m_previousMessage(0)
68     , m_expiredConsoleMessageCount(0)
69 {
70     m_instrumentingAgents->setInspectorConsoleAgent(this);
71 }
72
73 InspectorConsoleAgent::~InspectorConsoleAgent()
74 {
75     m_instrumentingAgents->setInspectorConsoleAgent(0);
76     m_instrumentingAgents = 0;
77     m_state = 0;
78     m_injectedScriptManager = 0;
79 }
80
81 void InspectorConsoleAgent::enable(ErrorString*)
82 {
83     m_state->setBoolean(ConsoleAgentState::consoleMessagesEnabled, true);
84
85     if (m_expiredConsoleMessageCount) {
86         ConsoleMessage expiredMessage(OtherMessageSource, LogMessageType, WarningMessageLevel, String::format("%d console messages are not shown.", m_expiredConsoleMessageCount), "", 0, "");
87         expiredMessage.addToFrontend(m_frontend, m_injectedScriptManager, false);
88     }
89
90     size_t messageCount = m_consoleMessages.size();
91     for (size_t i = 0; i < messageCount; ++i)
92         m_consoleMessages[i]->addToFrontend(m_frontend, m_injectedScriptManager, false);
93 }
94
95 void InspectorConsoleAgent::disable(ErrorString*)
96 {
97     m_state->setBoolean(ConsoleAgentState::consoleMessagesEnabled, false);
98 }
99
100 void InspectorConsoleAgent::clearMessages(ErrorString*)
101 {
102     m_consoleMessages.clear();
103     m_expiredConsoleMessageCount = 0;
104     m_previousMessage = 0;
105     m_injectedScriptManager->releaseObjectGroup("console");
106     if (m_frontend && m_state->getBoolean(ConsoleAgentState::consoleMessagesEnabled))
107         m_frontend->messagesCleared();
108 }
109
110 void InspectorConsoleAgent::reset()
111 {
112     ErrorString error;
113     clearMessages(&error);
114     m_times.clear();
115     m_counts.clear();
116 }
117
118 void InspectorConsoleAgent::restore()
119 {
120     if (m_state->getBoolean(ConsoleAgentState::consoleMessagesEnabled)) {
121         m_frontend->messagesCleared();
122         ErrorString error;
123         enable(&error);
124     }
125 }
126
127 void InspectorConsoleAgent::setFrontend(InspectorFrontend* frontend)
128 {
129     m_frontend = frontend->console();
130 }
131
132 void InspectorConsoleAgent::clearFrontend()
133 {
134     m_frontend = 0;
135     m_state->setBoolean(ConsoleAgentState::consoleMessagesEnabled, false);
136 }
137
138 void InspectorConsoleAgent::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, const String& message, PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack)
139 {
140     if (!developerExtrasEnabled())
141         return;
142     addConsoleMessage(adoptPtr(new ConsoleMessage(source, type, level, message, arguments, callStack)));
143 }
144
145 void InspectorConsoleAgent::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, const String& message, const String& scriptId, unsigned lineNumber)
146 {
147     if (!developerExtrasEnabled())
148         return;
149     addConsoleMessage(adoptPtr(new ConsoleMessage(source, type, level, message, scriptId, lineNumber)));
150 }
151
152 Vector<unsigned> InspectorConsoleAgent::consoleMessageArgumentCounts()
153 {
154     Vector<unsigned> result(m_consoleMessages.size());
155     for (size_t i = 0; i < m_consoleMessages.size(); i++)
156         result[i] = m_consoleMessages[i]->argumentCount();
157     return result;
158 }
159
160 void InspectorConsoleAgent::startTiming(const String& title)
161 {
162     // Follow Firebug's behavior of requiring a title that is not null or
163     // undefined for timing functions
164     if (title.isNull())
165         return;
166
167     m_times.add(title, monotonicallyIncreasingTime());
168 }
169
170 void InspectorConsoleAgent::stopTiming(const String& title, PassRefPtr<ScriptCallStack> callStack)
171 {
172     // Follow Firebug's behavior of requiring a title that is not null or
173     // undefined for timing functions
174     if (title.isNull())
175         return;
176
177     HashMap<String, double>::iterator it = m_times.find(title);
178     if (it == m_times.end())
179         return;
180
181     double startTime = it->second;
182     m_times.remove(it);
183
184     double elapsed = monotonicallyIncreasingTime() - startTime;
185     String message = title + String::format(": %.3fms", elapsed * 1000);
186     const ScriptCallFrame& lastCaller = callStack->at(0);
187     addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lastCaller.sourceURL(), lastCaller.lineNumber());
188 }
189
190 void InspectorConsoleAgent::count(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack)
191 {
192     const ScriptCallFrame& lastCaller = callStack->at(0);
193     // Follow Firebug's behavior of counting with null and undefined title in
194     // the same bucket as no argument
195     String title;
196     arguments->getFirstArgumentAsString(title);
197     String identifier = title + '@' + lastCaller.sourceURL() + ':' + String::number(lastCaller.lineNumber());
198
199     HashMap<String, unsigned>::iterator it = m_counts.find(identifier);
200     int count;
201     if (it == m_counts.end())
202         count = 1;
203     else {
204         count = it->second + 1;
205         m_counts.remove(it);
206     }
207
208     m_counts.add(identifier, count);
209
210     String message = title + ": " + String::number(count);
211     addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lastCaller.sourceURL(), lastCaller.lineNumber());
212 }
213
214 void InspectorConsoleAgent::frameWindowDiscarded(DOMWindow* window)
215 {
216     size_t messageCount = m_consoleMessages.size();
217     for (size_t i = 0; i < messageCount; ++i)
218         m_consoleMessages[i]->windowCleared(window);
219     m_injectedScriptManager->discardInjectedScriptsFor(window);
220 }
221
222 void InspectorConsoleAgent::resourceRetrievedByXMLHttpRequest(unsigned long identifier, const String& url, const String& sendURL, unsigned sendLineNumber)
223 {
224     if (!developerExtrasEnabled())
225         return;
226     if (m_frontend && m_state->getBoolean(ConsoleAgentState::monitoringXHR)) {
227         String message = "XHR finished loading: \"" + url + "\".";
228         String requestId = IdentifiersFactory::requestId(identifier);
229         addConsoleMessage(adoptPtr(new ConsoleMessage(NetworkMessageSource, LogMessageType, LogMessageLevel, message, sendURL, sendLineNumber, requestId)));
230     }
231
232
233 }
234
235 void InspectorConsoleAgent::didReceiveResponse(unsigned long identifier, const ResourceResponse& response)
236 {
237     if (!developerExtrasEnabled())
238         return;
239
240     if (response.httpStatusCode() >= 400) {
241         String message = "Failed to load resource: the server responded with a status of " + String::number(response.httpStatusCode()) + " (" + response.httpStatusText() + ')';
242         String requestId = IdentifiersFactory::requestId(identifier);
243         addConsoleMessage(adoptPtr(new ConsoleMessage(NetworkMessageSource, LogMessageType, ErrorMessageLevel, message, response.url().string(), requestId)));
244     }
245 }
246
247 void InspectorConsoleAgent::didFailLoading(unsigned long identifier, const ResourceError& error)
248 {
249     if (!developerExtrasEnabled())
250         return;
251     if (error.isCancellation()) // Report failures only.
252         return;
253     StringBuilder message;
254     message.appendLiteral("Failed to load resource");
255     if (!error.localizedDescription().isEmpty()) {
256         message.appendLiteral(": ");
257         message.append(error.localizedDescription());
258     }
259     String requestId = IdentifiersFactory::requestId(identifier);
260     addConsoleMessage(adoptPtr(new ConsoleMessage(NetworkMessageSource, LogMessageType, ErrorMessageLevel, message.toString(), error.failingURL(), requestId)));
261 }
262
263 void InspectorConsoleAgent::setMonitoringXHREnabled(ErrorString*, bool enabled)
264 {
265     m_state->setBoolean(ConsoleAgentState::monitoringXHR, enabled);
266 }
267
268 static bool isGroupMessage(MessageType type)
269 {
270     return type == StartGroupMessageType
271         || type ==  StartGroupCollapsedMessageType
272         || type == EndGroupMessageType;
273 }
274
275 void InspectorConsoleAgent::addConsoleMessage(PassOwnPtr<ConsoleMessage> consoleMessage)
276 {
277     ASSERT(developerExtrasEnabled());
278     ASSERT_ARG(consoleMessage, consoleMessage);
279
280     if (m_previousMessage && !isGroupMessage(m_previousMessage->type()) && m_previousMessage->isEqual(consoleMessage.get())) {
281         m_previousMessage->incrementCount();
282         if (m_frontend && m_state->getBoolean(ConsoleAgentState::consoleMessagesEnabled))
283             m_previousMessage->updateRepeatCountInConsole(m_frontend);
284     } else {
285         m_previousMessage = consoleMessage.get();
286         m_consoleMessages.append(consoleMessage);
287         if (m_frontend && m_state->getBoolean(ConsoleAgentState::consoleMessagesEnabled))
288             m_previousMessage->addToFrontend(m_frontend, m_injectedScriptManager, true);
289     }
290
291     if (!m_frontend && m_consoleMessages.size() >= maximumConsoleMessages) {
292         m_expiredConsoleMessageCount += expireConsoleMessagesStep;
293         m_consoleMessages.remove(0, expireConsoleMessagesStep);
294     }
295 }
296
297 class InspectableHeapObject : public InjectedScriptHost::InspectableObject {
298 public:
299     explicit InspectableHeapObject(int heapObjectId) : m_heapObjectId(heapObjectId) { }
300     virtual ScriptValue get(ScriptState*)
301     {
302         return ScriptProfiler::objectByHeapObjectId(m_heapObjectId);
303     }
304 private:
305     int m_heapObjectId;
306 };
307
308 void InspectorConsoleAgent::addInspectedHeapObject(ErrorString*, int inspectedHeapObjectId)
309 {
310     m_injectedScriptManager->injectedScriptHost()->addInspectedObject(adoptPtr(new InspectableHeapObject(inspectedHeapObjectId)));
311 }
312
313 } // namespace WebCore
314
315 #endif // ENABLE(INSPECTOR)