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