1 /*
2 * Copyright (C) 2007 Apple 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 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "config.h"
30 #include "Console.h"
31
32 #include "Chrome.h"
33 #include "ChromeClient.h"
34 #include "Frame.h"
35 #include "FrameLoader.h"
36 #include "FrameTree.h"
37 #include "InspectorConsoleInstrumentation.h"
38 #include "InspectorController.h"
39 #include "MemoryInfo.h"
40 #include "Page.h"
41 #include "PageGroup.h"
42 #include "PlatformString.h"
43 #include "ScriptArguments.h"
44 #include "ScriptCallStack.h"
45 #include "ScriptProfile.h"
46 #include "ScriptProfiler.h"
47 #include "ScriptValue.h"
48 #include <stdio.h>
49 #include <wtf/UnusedParam.h>
50 #include <wtf/text/CString.h>
51
52 namespace WebCore {
53
Console(Frame * frame)54 Console::Console(Frame* frame)
55 : m_frame(frame)
56 {
57 }
58
frame() const59 Frame* Console::frame() const
60 {
61 return m_frame;
62 }
63
disconnectFrame()64 void Console::disconnectFrame()
65 {
66 if (m_memory)
67 m_memory = 0;
68 m_frame = 0;
69 }
70
printSourceURLAndLine(const String & sourceURL,unsigned lineNumber)71 static void printSourceURLAndLine(const String& sourceURL, unsigned lineNumber)
72 {
73 if (!sourceURL.isEmpty()) {
74 if (lineNumber > 0)
75 printf("%s:%d: ", sourceURL.utf8().data(), lineNumber);
76 else
77 printf("%s: ", sourceURL.utf8().data());
78 }
79 }
80
printMessageSourceAndLevelPrefix(MessageSource source,MessageLevel level)81 static void printMessageSourceAndLevelPrefix(MessageSource source, MessageLevel level)
82 {
83 const char* sourceString;
84 switch (source) {
85 case HTMLMessageSource:
86 sourceString = "HTML";
87 break;
88 case XMLMessageSource:
89 sourceString = "XML";
90 break;
91 case JSMessageSource:
92 sourceString = "JS";
93 break;
94 case CSSMessageSource:
95 sourceString = "CSS";
96 break;
97 case OtherMessageSource:
98 sourceString = "OTHER";
99 break;
100 default:
101 ASSERT_NOT_REACHED();
102 sourceString = "UNKNOWN";
103 break;
104 }
105
106 const char* levelString;
107 switch (level) {
108 case TipMessageLevel:
109 levelString = "TIP";
110 break;
111 case LogMessageLevel:
112 levelString = "LOG";
113 break;
114 case WarningMessageLevel:
115 levelString = "WARN";
116 break;
117 case ErrorMessageLevel:
118 levelString = "ERROR";
119 break;
120 case DebugMessageLevel:
121 levelString = "DEBUG";
122 break;
123 default:
124 ASSERT_NOT_REACHED();
125 levelString = "UNKNOWN";
126 break;
127 }
128
129 printf("%s %s:", sourceString, levelString);
130 }
131
addMessage(MessageSource source,MessageType type,MessageLevel level,const String & message,unsigned lineNumber,const String & sourceURL)132 void Console::addMessage(MessageSource source, MessageType type, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceURL)
133 {
134 addMessage(source, type, level, message, lineNumber, sourceURL, 0);
135 }
136
addMessage(MessageSource source,MessageType type,MessageLevel level,const String & message,unsigned lineNumber,const String & sourceURL,PassRefPtr<ScriptCallStack> callStack)137 void Console::addMessage(MessageSource source, MessageType type, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceURL, PassRefPtr<ScriptCallStack> callStack)
138 {
139 Page* page = this->page();
140 if (!page)
141 return;
142
143 page->chrome()->client()->addMessageToConsole(source, type, level, message, lineNumber, sourceURL);
144
145 if (callStack)
146 InspectorInstrumentation::addMessageToConsole(page, source, type, level, message, 0, callStack);
147 else
148 InspectorInstrumentation::addMessageToConsole(page, source, type, level, message, lineNumber, sourceURL);
149
150 if (!Console::shouldPrintExceptions())
151 return;
152
153 printSourceURLAndLine(sourceURL, lineNumber);
154 printMessageSourceAndLevelPrefix(source, level);
155
156 printf(" %s\n", message.utf8().data());
157 }
158
addMessage(MessageType type,MessageLevel level,PassRefPtr<ScriptArguments> prpArguments,PassRefPtr<ScriptCallStack> prpCallStack,bool acceptNoArguments)159 void Console::addMessage(MessageType type, MessageLevel level, PassRefPtr<ScriptArguments> prpArguments, PassRefPtr<ScriptCallStack> prpCallStack, bool acceptNoArguments)
160 {
161 RefPtr<ScriptArguments> arguments = prpArguments;
162 RefPtr<ScriptCallStack> callStack = prpCallStack;
163
164 Page* page = this->page();
165 if (!page)
166 return;
167
168 const ScriptCallFrame& lastCaller = callStack->at(0);
169
170 if (!acceptNoArguments && !arguments->argumentCount())
171 return;
172
173 if (Console::shouldPrintExceptions()) {
174 printSourceURLAndLine(lastCaller.sourceURL(), 0);
175 printMessageSourceAndLevelPrefix(JSMessageSource, level);
176
177 for (unsigned i = 0; i < arguments->argumentCount(); ++i) {
178 String argAsString;
179 if (arguments->argumentAt(i).getString(arguments->globalState(), argAsString))
180 printf(" %s", argAsString.utf8().data());
181 }
182 printf("\n");
183 }
184
185 String message;
186 if (arguments->getFirstArgumentAsString(message))
187 page->chrome()->client()->addMessageToConsole(JSMessageSource, type, level, message, lastCaller.lineNumber(), lastCaller.sourceURL());
188
189 InspectorInstrumentation::addMessageToConsole(page, JSMessageSource, type, level, message, arguments, callStack);
190 }
191
debug(PassRefPtr<ScriptArguments> arguments,PassRefPtr<ScriptCallStack> callStack)192 void Console::debug(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack)
193 {
194 // In Firebug, console.debug has the same behavior as console.log. So we'll do the same.
195 log(arguments, callStack);
196 }
197
error(PassRefPtr<ScriptArguments> arguments,PassRefPtr<ScriptCallStack> callStack)198 void Console::error(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack)
199 {
200 addMessage(LogMessageType, ErrorMessageLevel, arguments, callStack);
201 }
202
info(PassRefPtr<ScriptArguments> arguments,PassRefPtr<ScriptCallStack> callStack)203 void Console::info(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack)
204 {
205 log(arguments, callStack);
206 }
207
log(PassRefPtr<ScriptArguments> arguments,PassRefPtr<ScriptCallStack> callStack)208 void Console::log(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack)
209 {
210 addMessage(LogMessageType, LogMessageLevel, arguments, callStack);
211 }
212
dir(PassRefPtr<ScriptArguments> arguments,PassRefPtr<ScriptCallStack> callStack)213 void Console::dir(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack)
214 {
215 addMessage(ObjectMessageType, LogMessageLevel, arguments, callStack);
216 }
217
dirxml(PassRefPtr<ScriptArguments> arguments,PassRefPtr<ScriptCallStack> callStack)218 void Console::dirxml(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack)
219 {
220 // The standard behavior of our console.log will print the DOM tree for nodes.
221 log(arguments, callStack);
222 }
223
trace(PassRefPtr<ScriptArguments> arguments,PassRefPtr<ScriptCallStack> prpCallStack)224 void Console::trace(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> prpCallStack)
225 {
226 RefPtr<ScriptCallStack> callStack = prpCallStack;
227 addMessage(TraceMessageType, LogMessageLevel, arguments, callStack, true);
228
229 if (!shouldPrintExceptions())
230 return;
231
232 printf("Stack Trace\n");
233 for (unsigned i = 0; i < callStack->size(); ++i) {
234 String functionName = String(callStack->at(i).functionName());
235 printf("\t%s\n", functionName.utf8().data());
236 }
237 }
238
assertCondition(bool condition,PassRefPtr<ScriptArguments> arguments,PassRefPtr<ScriptCallStack> callStack)239 void Console::assertCondition(bool condition, PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack)
240 {
241 if (condition)
242 return;
243
244 addMessage(AssertMessageType, ErrorMessageLevel, arguments, callStack, true);
245 }
246
count(PassRefPtr<ScriptArguments> arguments,PassRefPtr<ScriptCallStack> callStack)247 void Console::count(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack)
248 {
249 InspectorInstrumentation::consoleCount(page(), arguments, callStack);
250 }
251
markTimeline(PassRefPtr<ScriptArguments> arguments,PassRefPtr<ScriptCallStack>)252 void Console::markTimeline(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack>)
253 {
254 InspectorInstrumentation::consoleMarkTimeline(page(), arguments);
255 }
256
257 #if ENABLE(JAVASCRIPT_DEBUGGER)
258
profile(const String & title,ScriptState * state,PassRefPtr<ScriptCallStack> callStack)259 void Console::profile(const String& title, ScriptState* state, PassRefPtr<ScriptCallStack> callStack)
260 {
261 Page* page = this->page();
262 if (!page)
263 return;
264
265 // FIXME: log a console message when profiling is disabled.
266 if (!InspectorInstrumentation::profilerEnabled(page))
267 return;
268
269 String resolvedTitle = title;
270 if (title.isNull()) // no title so give it the next user initiated profile title.
271 resolvedTitle = InspectorInstrumentation::getCurrentUserInitiatedProfileName(page, true);
272
273 ScriptProfiler::start(state, resolvedTitle);
274
275 const ScriptCallFrame& lastCaller = callStack->at(0);
276 InspectorInstrumentation::addStartProfilingMessageToConsole(page, resolvedTitle, lastCaller.lineNumber(), lastCaller.sourceURL());
277 }
278
profileEnd(const String & title,ScriptState * state,PassRefPtr<ScriptCallStack> callStack)279 void Console::profileEnd(const String& title, ScriptState* state, PassRefPtr<ScriptCallStack> callStack)
280 {
281 Page* page = this->page();
282 if (!page)
283 return;
284
285 if (!InspectorInstrumentation::profilerEnabled(page))
286 return;
287
288 RefPtr<ScriptProfile> profile = ScriptProfiler::stop(state, title);
289 if (!profile)
290 return;
291
292 m_profiles.append(profile);
293 InspectorInstrumentation::addProfile(page, profile, callStack);
294 }
295
296 #endif
297
time(const String & title)298 void Console::time(const String& title)
299 {
300 InspectorInstrumentation::startConsoleTiming(page(), title);
301 }
302
timeEnd(const String & title,PassRefPtr<ScriptArguments>,PassRefPtr<ScriptCallStack> callStack)303 void Console::timeEnd(const String& title, PassRefPtr<ScriptArguments>, PassRefPtr<ScriptCallStack> callStack)
304 {
305 InspectorInstrumentation::stopConsoleTiming(page(), title, callStack);
306 }
307
group(PassRefPtr<ScriptArguments> arguments,PassRefPtr<ScriptCallStack> callStack)308 void Console::group(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack)
309 {
310 InspectorInstrumentation::addMessageToConsole(page(), JSMessageSource, StartGroupMessageType, LogMessageLevel, String(), arguments, callStack);
311 }
312
groupCollapsed(PassRefPtr<ScriptArguments> arguments,PassRefPtr<ScriptCallStack> callStack)313 void Console::groupCollapsed(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack)
314 {
315 InspectorInstrumentation::addMessageToConsole(page(), JSMessageSource, StartGroupCollapsedMessageType, LogMessageLevel, String(), arguments, callStack);
316 }
317
groupEnd()318 void Console::groupEnd()
319 {
320 InspectorInstrumentation::addMessageToConsole(page(), JSMessageSource, EndGroupMessageType, LogMessageLevel, String(), 0, String());
321 }
322
shouldCaptureFullStackTrace() const323 bool Console::shouldCaptureFullStackTrace() const
324 {
325 #if ENABLE(INSPECTOR)
326 Page* page = this->page();
327 if (!page)
328 return false;
329
330 return page->inspectorController()->hasFrontend();
331 #else
332 return false;
333 #endif
334 }
335
warn(PassRefPtr<ScriptArguments> arguments,PassRefPtr<ScriptCallStack> callStack)336 void Console::warn(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack)
337 {
338 addMessage(LogMessageType, WarningMessageLevel, arguments, callStack);
339 }
340
memory() const341 MemoryInfo* Console::memory() const
342 {
343 m_memory = MemoryInfo::create(m_frame);
344 return m_memory.get();
345 }
346
347 static bool printExceptions = false;
348
shouldPrintExceptions()349 bool Console::shouldPrintExceptions()
350 {
351 return printExceptions;
352 }
353
setShouldPrintExceptions(bool print)354 void Console::setShouldPrintExceptions(bool print)
355 {
356 printExceptions = print;
357 }
358
page() const359 Page* Console::page() const
360 {
361 if (!m_frame)
362 return 0;
363 return m_frame->page();
364 }
365
366 } // namespace WebCore
367