Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / 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 #include "core/inspector/InspectorConsoleAgent.h"
28
29 #include "bindings/v8/ScriptCallStackFactory.h"
30 #include "bindings/v8/ScriptController.h"
31 #include "bindings/v8/ScriptObject.h"
32 #include "bindings/v8/ScriptProfiler.h"
33 #include "core/frame/Frame.h"
34 #include "core/inspector/ConsoleMessage.h"
35 #include "core/inspector/InjectedScriptHost.h"
36 #include "core/inspector/InjectedScriptManager.h"
37 #include "core/inspector/InspectorState.h"
38 #include "core/inspector/InspectorTimelineAgent.h"
39 #include "core/inspector/InstrumentingAgents.h"
40 #include "core/inspector/ScriptArguments.h"
41 #include "core/inspector/ScriptCallFrame.h"
42 #include "core/inspector/ScriptCallStack.h"
43 #include "core/loader/DocumentLoader.h"
44 #include "core/page/Page.h"
45 #include "platform/network/ResourceError.h"
46 #include "platform/network/ResourceResponse.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 int InspectorConsoleAgent::s_enabledAgentCount = 0;
64
65 InspectorConsoleAgent::InspectorConsoleAgent(InspectorTimelineAgent* timelineAgent, InjectedScriptManager* injectedScriptManager)
66     : InspectorBaseAgent<InspectorConsoleAgent>("Console")
67     , m_timelineAgent(timelineAgent)
68     , m_injectedScriptManager(injectedScriptManager)
69     , m_frontend(0)
70     , m_previousMessage(0)
71     , m_expiredConsoleMessageCount(0)
72     , m_enabled(false)
73 {
74 }
75
76 InspectorConsoleAgent::~InspectorConsoleAgent()
77 {
78     m_instrumentingAgents->setInspectorConsoleAgent(0);
79     m_instrumentingAgents = 0;
80     m_state = 0;
81     m_injectedScriptManager = 0;
82 }
83
84 void InspectorConsoleAgent::init()
85 {
86     m_instrumentingAgents->setInspectorConsoleAgent(this);
87 }
88
89 void InspectorConsoleAgent::enable(ErrorString*)
90 {
91     if (m_enabled)
92         return;
93     m_enabled = true;
94     if (!s_enabledAgentCount)
95         ScriptController::setCaptureCallStackForUncaughtExceptions(true);
96     ++s_enabledAgentCount;
97
98     m_state->setBoolean(ConsoleAgentState::consoleMessagesEnabled, true);
99
100     if (m_expiredConsoleMessageCount) {
101         ConsoleMessage expiredMessage(!isWorkerAgent(), OtherMessageSource, LogMessageType, WarningMessageLevel, String::format("%d console messages are not shown.", m_expiredConsoleMessageCount));
102         expiredMessage.setTimestamp(0);
103         expiredMessage.addToFrontend(m_frontend, m_injectedScriptManager, false);
104     }
105
106     size_t messageCount = m_consoleMessages.size();
107     for (size_t i = 0; i < messageCount; ++i)
108         m_consoleMessages[i]->addToFrontend(m_frontend, m_injectedScriptManager, false);
109 }
110
111 void InspectorConsoleAgent::disable(ErrorString*)
112 {
113     if (!m_enabled)
114         return;
115     m_enabled = false;
116     if (!(--s_enabledAgentCount))
117         ScriptController::setCaptureCallStackForUncaughtExceptions(false);
118     m_state->setBoolean(ConsoleAgentState::consoleMessagesEnabled, false);
119 }
120
121 void InspectorConsoleAgent::clearMessages(ErrorString*)
122 {
123     m_consoleMessages.clear();
124     m_expiredConsoleMessageCount = 0;
125     m_previousMessage = 0;
126     m_injectedScriptManager->releaseObjectGroup("console");
127     if (m_frontend && m_enabled)
128         m_frontend->messagesCleared();
129 }
130
131 void InspectorConsoleAgent::reset()
132 {
133     ErrorString error;
134     clearMessages(&error);
135     m_times.clear();
136     m_counts.clear();
137 }
138
139 void InspectorConsoleAgent::restore()
140 {
141     if (m_state->getBoolean(ConsoleAgentState::consoleMessagesEnabled)) {
142         m_frontend->messagesCleared();
143         ErrorString error;
144         enable(&error);
145     }
146 }
147
148 void InspectorConsoleAgent::setFrontend(InspectorFrontend* frontend)
149 {
150     m_frontend = frontend->console();
151 }
152
153 void InspectorConsoleAgent::clearFrontend()
154 {
155     m_frontend = 0;
156     String errorString;
157     disable(&errorString);
158 }
159
160 void InspectorConsoleAgent::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, const String& message, PassRefPtr<ScriptCallStack> callStack, unsigned long requestIdentifier)
161 {
162     if (type == ClearMessageType) {
163         ErrorString error;
164         clearMessages(&error);
165     }
166
167     addConsoleMessage(adoptPtr(new ConsoleMessage(!isWorkerAgent(), source, type, level, message, callStack, requestIdentifier)));
168 }
169
170 void InspectorConsoleAgent::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, const String& message, ScriptState* state, PassRefPtr<ScriptArguments> arguments, unsigned long requestIdentifier)
171 {
172     if (type == ClearMessageType) {
173         ErrorString error;
174         clearMessages(&error);
175     }
176
177     addConsoleMessage(adoptPtr(new ConsoleMessage(!isWorkerAgent(), source, type, level, message, arguments, state, requestIdentifier)));
178 }
179
180 void InspectorConsoleAgent::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, const String& message, const String& scriptId, unsigned lineNumber, unsigned columnNumber, ScriptState* state, unsigned long requestIdentifier)
181 {
182     if (type == ClearMessageType) {
183         ErrorString error;
184         clearMessages(&error);
185     }
186
187     bool canGenerateCallStack = !isWorkerAgent() && m_frontend;
188     addConsoleMessage(adoptPtr(new ConsoleMessage(canGenerateCallStack, source, type, level, message, scriptId, lineNumber, columnNumber, state, requestIdentifier)));
189 }
190
191 Vector<unsigned> InspectorConsoleAgent::consoleMessageArgumentCounts()
192 {
193     Vector<unsigned> result(m_consoleMessages.size());
194     for (size_t i = 0; i < m_consoleMessages.size(); i++)
195         result[i] = m_consoleMessages[i]->argumentCount();
196     return result;
197 }
198
199 void InspectorConsoleAgent::consoleTime(ExecutionContext*, const String& title)
200 {
201     // Follow Firebug's behavior of requiring a title that is not null or
202     // undefined for timing functions
203     if (title.isNull())
204         return;
205
206     m_times.add(title, monotonicallyIncreasingTime());
207 }
208
209 void InspectorConsoleAgent::consoleTimeEnd(ExecutionContext*, const String& title, ScriptState* state)
210 {
211     // Follow Firebug's behavior of requiring a title that is not null or
212     // undefined for timing functions
213     if (title.isNull())
214         return;
215
216     HashMap<String, double>::iterator it = m_times.find(title);
217     if (it == m_times.end())
218         return;
219
220     double startTime = it->value;
221     m_times.remove(it);
222
223     double elapsed = monotonicallyIncreasingTime() - startTime;
224     String message = title + String::format(": %.3fms", elapsed * 1000);
225     addMessageToConsole(ConsoleAPIMessageSource, LogMessageType, DebugMessageLevel, message, String(), 0, 0, state);
226 }
227
228 void InspectorConsoleAgent::consoleTimeline(ExecutionContext* context, const String& title, ScriptState* state)
229 {
230     m_timelineAgent->consoleTimeline(context, title, state);
231 }
232
233 void InspectorConsoleAgent::consoleTimelineEnd(ExecutionContext* context, const String& title, ScriptState* state)
234 {
235     m_timelineAgent->consoleTimelineEnd(context, title, state);
236 }
237
238 void InspectorConsoleAgent::consoleCount(ScriptState* state, PassRefPtr<ScriptArguments> arguments)
239 {
240     RefPtr<ScriptCallStack> callStack(createScriptCallStackForConsole());
241     const ScriptCallFrame& lastCaller = callStack->at(0);
242     // Follow Firebug's behavior of counting with null and undefined title in
243     // the same bucket as no argument
244     String title;
245     arguments->getFirstArgumentAsString(title);
246     String identifier = title.isEmpty() ? String(lastCaller.sourceURL() + ':' + String::number(lastCaller.lineNumber()))
247                                         : String(title + '@');
248
249     HashCountedSet<String>::AddResult result = m_counts.add(identifier);
250     String message = title + ": " + String::number(result.storedValue->value);
251     addMessageToConsole(ConsoleAPIMessageSource, LogMessageType, DebugMessageLevel, message, callStack);
252 }
253
254 void InspectorConsoleAgent::frameWindowDiscarded(DOMWindow* window)
255 {
256     size_t messageCount = m_consoleMessages.size();
257     for (size_t i = 0; i < messageCount; ++i)
258         m_consoleMessages[i]->windowCleared(window);
259     m_injectedScriptManager->discardInjectedScriptsFor(window);
260 }
261
262 void InspectorConsoleAgent::didCommitLoad(Frame* frame, DocumentLoader* loader)
263 {
264     if (loader->frame() != frame->page()->mainFrame())
265         return;
266     reset();
267 }
268
269 void InspectorConsoleAgent::didFinishXHRLoading(XMLHttpRequest*, ThreadableLoaderClient*, unsigned long requestIdentifier, ScriptString, const AtomicString& method, const String& url, const String& sendURL, unsigned sendLineNumber)
270 {
271     if (m_frontend && m_state->getBoolean(ConsoleAgentState::monitoringXHR)) {
272         String message = "XHR finished loading: " + method + " \"" + url + "\".";
273         addMessageToConsole(NetworkMessageSource, LogMessageType, DebugMessageLevel, message, sendURL, sendLineNumber, 0, 0, requestIdentifier);
274     }
275 }
276
277 void InspectorConsoleAgent::didReceiveResourceResponse(Frame*, unsigned long requestIdentifier, DocumentLoader* loader, const ResourceResponse& response, ResourceLoader* resourceLoader)
278 {
279     if (!loader)
280         return;
281     if (response.httpStatusCode() >= 400) {
282         String message = "Failed to load resource: the server responded with a status of " + String::number(response.httpStatusCode()) + " (" + response.httpStatusText() + ')';
283         addMessageToConsole(NetworkMessageSource, LogMessageType, ErrorMessageLevel, message, response.url().string(), 0, 0, 0, requestIdentifier);
284     }
285 }
286
287 void InspectorConsoleAgent::didFailLoading(unsigned long requestIdentifier, const ResourceError& error)
288 {
289     if (error.isCancellation()) // Report failures only.
290         return;
291     StringBuilder message;
292     message.appendLiteral("Failed to load resource");
293     if (!error.localizedDescription().isEmpty()) {
294         message.appendLiteral(": ");
295         message.append(error.localizedDescription());
296     }
297     addMessageToConsole(NetworkMessageSource, LogMessageType, ErrorMessageLevel, message.toString(), error.failingURL(), 0, 0, 0, requestIdentifier);
298 }
299
300 void InspectorConsoleAgent::setMonitoringXHREnabled(ErrorString*, bool enabled)
301 {
302     m_state->setBoolean(ConsoleAgentState::monitoringXHR, enabled);
303 }
304
305 static bool isGroupMessage(MessageType type)
306 {
307     return type == StartGroupMessageType
308         || type ==  StartGroupCollapsedMessageType
309         || type == EndGroupMessageType;
310 }
311
312 void InspectorConsoleAgent::addConsoleMessage(PassOwnPtr<ConsoleMessage> consoleMessage)
313 {
314     ASSERT_ARG(consoleMessage, consoleMessage);
315
316     if (m_previousMessage && !isGroupMessage(m_previousMessage->type()) && m_previousMessage->isEqual(consoleMessage.get())) {
317         m_previousMessage->incrementCount();
318         if (m_frontend && m_enabled)
319             m_previousMessage->updateRepeatCountInConsole(m_frontend);
320     } else {
321         m_previousMessage = consoleMessage.get();
322         m_consoleMessages.append(consoleMessage);
323         if (m_frontend && m_enabled)
324             m_previousMessage->addToFrontend(m_frontend, m_injectedScriptManager, true);
325     }
326
327     if (!m_frontend && m_consoleMessages.size() >= maximumConsoleMessages) {
328         m_expiredConsoleMessageCount += expireConsoleMessagesStep;
329         m_consoleMessages.remove(0, expireConsoleMessagesStep);
330     }
331 }
332
333 class InspectableHeapObject FINAL : public InjectedScriptHost::InspectableObject {
334 public:
335     explicit InspectableHeapObject(int heapObjectId) : m_heapObjectId(heapObjectId) { }
336     virtual ScriptValue get(ScriptState*) OVERRIDE
337     {
338         return ScriptProfiler::objectByHeapObjectId(m_heapObjectId);
339     }
340 private:
341     int m_heapObjectId;
342 };
343
344 void InspectorConsoleAgent::addInspectedHeapObject(ErrorString*, int inspectedHeapObjectId)
345 {
346     m_injectedScriptManager->injectedScriptHost()->addInspectedObject(adoptPtr(new InspectableHeapObject(inspectedHeapObjectId)));
347 }
348
349 } // namespace WebCore
350