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