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 "ChromeClient.h"
33 #include "CString.h"
34 #include "Frame.h"
35 #include "FrameLoader.h"
36 #include "FrameTree.h"
37 #include "InspectorController.h"
38 #include "Page.h"
39 #include "PageGroup.h"
40 #include "PlatformString.h"
41
42 #if USE(JSC)
43 #include <profiler/Profiler.h>
44 #endif
45
46 #include "ScriptCallStack.h"
47 #include <stdio.h>
48
49 namespace WebCore {
50
Console(Frame * frame)51 Console::Console(Frame* frame)
52 : m_frame(frame)
53 {
54 }
55
disconnectFrame()56 void Console::disconnectFrame()
57 {
58 m_frame = 0;
59 }
60
printSourceURLAndLine(const String & sourceURL,unsigned lineNumber)61 static void printSourceURLAndLine(const String& sourceURL, unsigned lineNumber)
62 {
63 if (!sourceURL.isEmpty()) {
64 if (lineNumber > 0)
65 printf("%s:%d: ", sourceURL.utf8().data(), lineNumber);
66 else
67 printf("%s: ", sourceURL.utf8().data());
68 }
69 }
70
getFirstArgumentAsString(const ScriptCallFrame & callFrame,String & result,bool checkForNullOrUndefined=false)71 static bool getFirstArgumentAsString(const ScriptCallFrame& callFrame, String& result, bool checkForNullOrUndefined = false)
72 {
73 if (!callFrame.argumentCount())
74 return false;
75
76 const ScriptValue& value = callFrame.argumentAt(0);
77 if (checkForNullOrUndefined && (value.isNull() || value.isUndefined()))
78 return false;
79
80 return value.getString(result);
81 }
82
printMessageSourceAndLevelPrefix(MessageSource source,MessageLevel level)83 static void printMessageSourceAndLevelPrefix(MessageSource source, MessageLevel level)
84 {
85 const char* sourceString;
86 switch (source) {
87 case HTMLMessageSource:
88 sourceString = "HTML";
89 break;
90 case WMLMessageSource:
91 sourceString = "WML";
92 break;
93 case XMLMessageSource:
94 sourceString = "XML";
95 break;
96 case JSMessageSource:
97 sourceString = "JS";
98 break;
99 case CSSMessageSource:
100 sourceString = "CSS";
101 break;
102 default:
103 ASSERT_NOT_REACHED();
104 // Fall thru.
105 case OtherMessageSource:
106 sourceString = "OTHER";
107 break;
108 }
109
110 const char* levelString;
111 switch (level) {
112 case TipMessageLevel:
113 levelString = "TIP";
114 break;
115 default:
116 ASSERT_NOT_REACHED();
117 // Fall thru.
118 case LogMessageLevel:
119 levelString = "LOG";
120 break;
121 case WarningMessageLevel:
122 levelString = "WARN";
123 break;
124 case ErrorMessageLevel:
125 levelString = "ERROR";
126 break;
127 }
128
129 printf("%s %s:", sourceString, levelString);
130 }
131
addMessage(MessageSource source,MessageLevel level,const String & message,unsigned lineNumber,const String & sourceURL)132 void Console::addMessage(MessageSource source, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceURL)
133 {
134 Page* page = this->page();
135 if (!page)
136 return;
137
138 if (source == JSMessageSource || source == WMLMessageSource)
139 page->chrome()->client()->addMessageToConsole(message, lineNumber, sourceURL);
140
141 page->inspectorController()->addMessageToConsole(source, level, message, lineNumber, sourceURL);
142
143 if (!Console::shouldPrintExceptions())
144 return;
145
146 printSourceURLAndLine(sourceURL, lineNumber);
147 printMessageSourceAndLevelPrefix(source, level);
148
149 printf(" %s\n", message.utf8().data());
150 }
151
addMessage(MessageLevel level,ScriptCallStack * callStack,bool acceptNoArguments)152 void Console::addMessage(MessageLevel level, ScriptCallStack* callStack, bool acceptNoArguments) {
153 Page* page = this->page();
154 if (!page)
155 return;
156
157 const ScriptCallFrame& lastCaller = callStack->at(0);
158
159 if (!acceptNoArguments && !lastCaller.argumentCount())
160 return;
161
162 String message;
163 if (getFirstArgumentAsString(lastCaller, message))
164 page->chrome()->client()->addMessageToConsole(message, lastCaller.lineNumber(), lastCaller.sourceURL().prettyURL());
165
166 page->inspectorController()->addMessageToConsole(JSMessageSource, level, callStack);
167
168 if (!Console::shouldPrintExceptions())
169 return;
170
171 printSourceURLAndLine(lastCaller.sourceURL().prettyURL(), 0);
172 printMessageSourceAndLevelPrefix(JSMessageSource, level);
173
174 for (unsigned i = 0; i < lastCaller.argumentCount(); ++i) {
175 String argAsString;
176 if (lastCaller.argumentAt(i).getString(argAsString))
177 printf(" %s", argAsString.utf8().data());
178 }
179 printf("\n");
180 }
181
debug(ScriptCallStack * callStack)182 void Console::debug(ScriptCallStack* callStack)
183 {
184 // In Firebug, console.debug has the same behavior as console.log. So we'll do the same.
185 log(callStack);
186 }
187
error(ScriptCallStack * callStack)188 void Console::error(ScriptCallStack* callStack)
189 {
190 addMessage(ErrorMessageLevel, callStack);
191 }
192
info(ScriptCallStack * callStack)193 void Console::info(ScriptCallStack* callStack)
194 {
195 log(callStack);
196 }
197
log(ScriptCallStack * callStack)198 void Console::log(ScriptCallStack* callStack)
199 {
200 addMessage(LogMessageLevel, callStack);
201 }
202
dir(ScriptCallStack * callStack)203 void Console::dir(ScriptCallStack* callStack)
204 {
205 addMessage(ObjectMessageLevel, callStack);
206 }
207
dirxml(ScriptCallStack * callStack)208 void Console::dirxml(ScriptCallStack* callStack)
209 {
210 addMessage(NodeMessageLevel, callStack);
211 }
212
trace(ScriptCallStack * callStack)213 void Console::trace(ScriptCallStack* callStack)
214 {
215 addMessage(TraceMessageLevel, callStack, true);
216
217 if (!shouldPrintExceptions())
218 return;
219
220 printf("Stack Trace\n");
221 for (unsigned i = 0; i < callStack->size(); ++i) {
222 String functionName = String(callStack->at(i).functionName());
223 printf("\t%s\n", functionName.utf8().data());
224 }
225 }
226
assertCondition(bool condition,ScriptCallStack * callStack)227 void Console::assertCondition(bool condition, ScriptCallStack* callStack)
228 {
229 if (condition)
230 return;
231
232 // FIXME: <https://bugs.webkit.org/show_bug.cgi?id=19135> It would be nice to prefix assertion failures with a message like "Assertion failed: ".
233 addMessage(ErrorMessageLevel, callStack, true);
234 }
235
count(ScriptCallStack * callStack)236 void Console::count(ScriptCallStack* callStack)
237 {
238 Page* page = this->page();
239 if (!page)
240 return;
241
242 const ScriptCallFrame& lastCaller = callStack->at(0);
243 // Follow Firebug's behavior of counting with null and undefined title in
244 // the same bucket as no argument
245 String title;
246 getFirstArgumentAsString(lastCaller, title);
247
248 page->inspectorController()->count(title, lastCaller.lineNumber(), lastCaller.sourceURL().string());
249 }
250
251 #if USE(JSC)
252
profile(const JSC::UString & title,ScriptCallStack * callStack)253 void Console::profile(const JSC::UString& title, ScriptCallStack* callStack)
254 {
255 Page* page = this->page();
256 if (!page)
257 return;
258
259 if (title.isNull())
260 return;
261
262 // FIXME: log a console message when profiling is disabled.
263 if (!page->inspectorController()->profilerEnabled())
264 return;
265
266 JSC::Profiler::profiler()->startProfiling(callStack->state(), title);
267 }
268
profileEnd(const JSC::UString & title,ScriptCallStack * callStack)269 void Console::profileEnd(const JSC::UString& title, ScriptCallStack* callStack)
270 {
271 Page* page = this->page();
272 if (!page)
273 return;
274
275 if (!page->inspectorController()->profilerEnabled())
276 return;
277
278 RefPtr<JSC::Profile> profile = JSC::Profiler::profiler()->stopProfiling(callStack->state(), title);
279 if (!profile)
280 return;
281
282 m_profiles.append(profile);
283
284 if (Page* page = this->page()) {
285 const ScriptCallFrame& lastCaller = callStack->at(0);
286 page->inspectorController()->addProfile(profile, lastCaller.lineNumber(), lastCaller.sourceURL());
287 }
288 }
289
290 #endif
291
time(const String & title)292 void Console::time(const String& title)
293 {
294 Page* page = this->page();
295 if (!page)
296 return;
297
298 // Follow Firebug's behavior of requiring a title that is not null or
299 // undefined for timing functions
300 if (title.isNull())
301 return;
302
303 page->inspectorController()->startTiming(title);
304 }
305
timeEnd(const String & title,ScriptCallStack * callStack)306 void Console::timeEnd(const String& title, ScriptCallStack* callStack)
307 {
308 Page* page = this->page();
309 if (!page)
310 return;
311
312 // Follow Firebug's behavior of requiring a title that is not null or
313 // undefined for timing functions
314 if (title.isNull())
315 return;
316
317 double elapsed;
318 if (!page->inspectorController()->stopTiming(title, elapsed))
319 return;
320
321 String message = title + String::format(": %.0fms", elapsed);
322
323 const ScriptCallFrame& lastCaller = callStack->at(0);
324 page->inspectorController()->addMessageToConsole(JSMessageSource, LogMessageLevel, message, lastCaller.lineNumber(), lastCaller.sourceURL().string());
325 }
326
group(ScriptCallStack * callStack)327 void Console::group(ScriptCallStack* callStack)
328 {
329 Page* page = this->page();
330 if (!page)
331 return;
332
333 page->inspectorController()->startGroup(JSMessageSource, callStack);
334 }
335
groupEnd()336 void Console::groupEnd()
337 {
338 Page* page = this->page();
339 if (!page)
340 return;
341
342 page->inspectorController()->endGroup(JSMessageSource, 0, String());
343 }
344
warn(ScriptCallStack * callStack)345 void Console::warn(ScriptCallStack* callStack)
346 {
347 addMessage(WarningMessageLevel, callStack);
348 }
349
350 static bool printExceptions = false;
351
shouldPrintExceptions()352 bool Console::shouldPrintExceptions()
353 {
354 return printExceptions;
355 }
356
setShouldPrintExceptions(bool print)357 void Console::setShouldPrintExceptions(bool print)
358 {
359 printExceptions = print;
360 }
361
page() const362 Page* Console::page() const
363 {
364 if (!m_frame)
365 return 0;
366 return m_frame->page();
367 }
368
369 } // namespace WebCore
370