9cc8d5ddbd4b428861f33d12b290e91427e20738
[WebKit-https.git] / WebCore / page / Console.cpp
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 "ConsoleMessage.h"
35 #include "Frame.h"
36 #include "FrameLoader.h"
37 #include "FrameTree.h"
38 #include "InspectorController.h"
39 #include "MemoryInfo.h"
40 #include "Page.h"
41 #include "PageGroup.h"
42 #include "PlatformString.h"
43
44 #include "ScriptCallStack.h"
45 #include "ScriptProfile.h"
46 #include "ScriptProfiler.h"
47 #include <stdio.h>
48 #include <wtf/text/CString.h>
49 #include <wtf/UnusedParam.h>
50
51 namespace WebCore {
52
53 Console::Console(Frame* frame)
54     : m_frame(frame)
55 {
56 }
57
58 Frame* Console::frame() const
59 {
60     return m_frame;
61 }
62
63 void Console::disconnectFrame()
64 {
65     if (m_memory)
66         m_memory = 0;
67     m_frame = 0;
68 }
69
70 static void printSourceURLAndLine(const String& sourceURL, unsigned lineNumber)
71 {
72     if (!sourceURL.isEmpty()) {
73         if (lineNumber > 0)
74             printf("%s:%d: ", sourceURL.utf8().data(), lineNumber);
75         else
76             printf("%s: ", sourceURL.utf8().data());
77     }
78 }
79
80 static bool getFirstArgumentAsString(ScriptState* scriptState, const ScriptCallFrame& callFrame, String& result, bool checkForNullOrUndefined = false)
81 {
82     if (!callFrame.argumentCount())
83         return false;
84
85     const ScriptValue& value = callFrame.argumentAt(0);
86     if (checkForNullOrUndefined && (value.isNull() || value.isUndefined()))
87         return false;
88
89     result = value.toString(scriptState);
90     return true;
91 }
92
93 static void printMessageSourceAndLevelPrefix(MessageSource source, MessageLevel level)
94 {
95     const char* sourceString;
96     switch (source) {
97     case HTMLMessageSource:
98         sourceString = "HTML";
99         break;
100     case WMLMessageSource:
101         sourceString = "WML";
102         break;
103     case XMLMessageSource:
104         sourceString = "XML";
105         break;
106     case JSMessageSource:
107         sourceString = "JS";
108         break;
109     case CSSMessageSource:
110         sourceString = "CSS";
111         break;
112     case OtherMessageSource:
113         sourceString = "OTHER";
114         break;
115     default:
116         ASSERT_NOT_REACHED();
117         sourceString = "UNKNOWN";
118         break;
119     }
120
121     const char* levelString;
122     switch (level) {
123     case TipMessageLevel:
124         levelString = "TIP";
125         break;
126     case LogMessageLevel:
127         levelString = "LOG";
128         break;
129     case WarningMessageLevel:
130         levelString = "WARN";
131         break;
132     case ErrorMessageLevel:
133         levelString = "ERROR";
134         break;
135     case DebugMessageLevel:
136         levelString = "DEBUG";
137         break;
138     default:
139         ASSERT_NOT_REACHED();
140         levelString = "UNKNOWN";
141         break;
142     }
143
144     printf("%s %s:", sourceString, levelString);
145 }
146
147 void Console::addMessage(MessageSource source, MessageType type, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceURL, ScriptCallStack* callStack)
148 {
149     Page* page = this->page();
150     if (!page)
151         return;
152
153     if (source == JSMessageSource)
154         page->chrome()->client()->addMessageToConsole(source, type, level, message, lineNumber, sourceURL);
155
156 #if ENABLE(INSPECTOR)
157     if (callStack)
158         page->inspectorController()->addMessageToConsole(source, type, level, callStack, message);
159     else
160         page->inspectorController()->addMessageToConsole(source, type, level, message, lineNumber, sourceURL);
161 #endif
162
163     if (!Console::shouldPrintExceptions())
164         return;
165
166     printSourceURLAndLine(sourceURL, lineNumber);
167     printMessageSourceAndLevelPrefix(source, level);
168
169     printf(" %s\n", message.utf8().data());
170 }
171
172 void Console::addMessage(MessageType type, MessageLevel level, ScriptCallStack* callStack, bool acceptNoArguments)
173 {
174     Page* page = this->page();
175     if (!page)
176         return;
177
178     const ScriptCallFrame& lastCaller = callStack->at(0);
179
180     if (!acceptNoArguments && !lastCaller.argumentCount())
181         return;
182
183     String message;
184     if (getFirstArgumentAsString(callStack->state(), lastCaller, message))
185         page->chrome()->client()->addMessageToConsole(JSMessageSource, type, level, message, lastCaller.lineNumber(), lastCaller.sourceURL().prettyURL());
186
187 #if ENABLE(INSPECTOR)
188     page->inspectorController()->addMessageToConsole(JSMessageSource, type, level, callStack, message);
189 #endif
190
191     if (!Console::shouldPrintExceptions())
192         return;
193
194     printSourceURLAndLine(lastCaller.sourceURL().prettyURL(), 0);
195     printMessageSourceAndLevelPrefix(JSMessageSource, level);
196
197     for (unsigned i = 0; i < lastCaller.argumentCount(); ++i) {
198         String argAsString;
199         if (lastCaller.argumentAt(i).getString(callStack->state(), argAsString))
200             printf(" %s", argAsString.utf8().data());
201     }
202     printf("\n");
203 }
204
205 void Console::debug(ScriptCallStack* callStack)
206 {
207     // In Firebug, console.debug has the same behavior as console.log. So we'll do the same.
208     log(callStack);
209 }
210
211 void Console::error(ScriptCallStack* callStack)
212 {
213     addMessage(LogMessageType, ErrorMessageLevel, callStack);
214 }
215
216 void Console::info(ScriptCallStack* callStack)
217 {
218     log(callStack);
219 }
220
221 void Console::log(ScriptCallStack* callStack)
222 {
223     addMessage(LogMessageType, LogMessageLevel, callStack);
224 }
225
226 void Console::dir(ScriptCallStack* callStack)
227 {
228     addMessage(ObjectMessageType, LogMessageLevel, callStack);
229 }
230
231 void Console::dirxml(ScriptCallStack* callStack)
232 {
233     // The standard behavior of our console.log will print the DOM tree for nodes.
234     log(callStack);
235 }
236
237 void Console::trace(ScriptCallStack* callStack)
238 {
239     addMessage(TraceMessageType, LogMessageLevel, callStack, true);
240
241     if (!shouldPrintExceptions())
242         return;
243
244     printf("Stack Trace\n");
245     for (unsigned i = 0; i < callStack->size(); ++i) {
246         String functionName = String(callStack->at(i).functionName());
247         printf("\t%s\n", functionName.utf8().data());
248     }
249 }
250
251 void Console::assertCondition(bool condition, ScriptCallStack* callStack)
252 {
253     if (condition)
254         return;
255
256     addMessage(AssertMessageType, ErrorMessageLevel, callStack, true);
257 }
258
259 void Console::count(ScriptCallStack* callStack)
260 {
261 #if ENABLE(INSPECTOR)
262     Page* page = this->page();
263     if (!page)
264         return;
265
266     const ScriptCallFrame& lastCaller = callStack->at(0);
267     // Follow Firebug's behavior of counting with null and undefined title in
268     // the same bucket as no argument
269     String title;
270     getFirstArgumentAsString(callStack->state(), lastCaller, title);
271
272     page->inspectorController()->count(title, lastCaller.lineNumber(), lastCaller.sourceURL().string());
273 #else
274     UNUSED_PARAM(callStack);
275 #endif
276 }
277
278 void Console::markTimeline(ScriptCallStack* callStack)
279 {
280 #if ENABLE(INSPECTOR)
281     Page* page = this->page();
282     if (!page)
283         return;
284
285     const ScriptCallFrame& lastCaller = callStack->at(0);
286     String message;
287     getFirstArgumentAsString(callStack->state(), lastCaller, message);
288
289     page->inspectorController()->markTimeline(message);
290 #else
291     UNUSED_PARAM(callStack);
292 #endif
293 }
294
295 #if ENABLE(WML)
296 String Console::lastWMLErrorMessage() const
297 {
298 #if ENABLE(INSPECTOR)
299     Page* page = this->page();
300     if (!page)
301         return String();
302
303     const Vector<OwnPtr<ConsoleMessage> >& consoleMessages = page->inspectorController()->consoleMessages();
304     if (consoleMessages.isEmpty())
305         return String();
306
307     Vector<OwnPtr<ConsoleMessage> >::const_iterator it = consoleMessages.begin();
308     const Vector<OwnPtr<ConsoleMessage> >::const_iterator end = consoleMessages.end();
309
310     for (; it != end; ++it) {
311         ConsoleMessage* message = it->get();
312         if (message->source() != WMLMessageSource)
313             continue;
314
315         return message->message();
316     }
317 #endif
318     return String();
319 }
320 #endif
321
322 #if ENABLE(JAVASCRIPT_DEBUGGER)
323
324 void Console::profile(const String& title, ScriptCallStack* callStack)
325 {
326     Page* page = this->page();
327     if (!page)
328         return;
329
330 #if ENABLE(INSPECTOR)
331     InspectorController* controller = page->inspectorController();
332     // FIXME: log a console message when profiling is disabled.
333     if (!controller->profilerEnabled())
334         return;
335 #endif
336
337     String resolvedTitle = title;
338     if (title.isNull()) // no title so give it the next user initiated profile title.
339 #if ENABLE(INSPECTOR)
340         resolvedTitle = controller->getCurrentUserInitiatedProfileName(true);
341 #else
342         resolvedTitle = "";
343 #endif
344
345     ScriptProfiler::start(callStack->state(), resolvedTitle);
346
347 #if ENABLE(INSPECTOR)
348     const ScriptCallFrame& lastCaller = callStack->at(0);
349     controller->addStartProfilingMessageToConsole(resolvedTitle, lastCaller.lineNumber(), lastCaller.sourceURL());
350 #endif
351 }
352
353 void Console::profileEnd(const String& title, ScriptCallStack* callStack)
354 {
355     Page* page = this->page();
356     if (!page)
357         return;
358
359 #if ENABLE(INSPECTOR)
360     InspectorController* controller = page->inspectorController();
361     if (!controller->profilerEnabled())
362         return;
363 #endif
364
365     RefPtr<ScriptProfile> profile = ScriptProfiler::stop(callStack->state(), title);
366     if (!profile)
367         return;
368
369     m_profiles.append(profile);
370
371 #if ENABLE(INSPECTOR)
372     const ScriptCallFrame& lastCaller = callStack->at(0);
373     controller->addProfile(profile, lastCaller.lineNumber(), lastCaller.sourceURL());
374 #endif
375 }
376
377 #endif
378
379 void Console::time(const String& title)
380 {
381 #if ENABLE(INSPECTOR)
382     Page* page = this->page();
383     if (!page)
384         return;
385
386     // Follow Firebug's behavior of requiring a title that is not null or
387     // undefined for timing functions
388     if (title.isNull())
389         return;
390
391     page->inspectorController()->startTiming(title);
392 #else
393     UNUSED_PARAM(title);
394 #endif
395 }
396
397 void Console::timeEnd(const String& title, ScriptCallStack* callStack)
398 {
399 #if ENABLE(INSPECTOR)
400     Page* page = this->page();
401     if (!page)
402         return;
403
404     // Follow Firebug's behavior of requiring a title that is not null or
405     // undefined for timing functions
406     if (title.isNull())
407         return;
408
409     double elapsed;
410     if (!page->inspectorController()->stopTiming(title, elapsed))
411         return;
412
413     String message = title + String::format(": %.0fms", elapsed);
414
415     const ScriptCallFrame& lastCaller = callStack->at(0);
416     page->inspectorController()->addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lastCaller.lineNumber(), lastCaller.sourceURL().string());
417 #else
418     UNUSED_PARAM(title);
419     UNUSED_PARAM(callStack);
420 #endif
421 }
422
423 void Console::group(ScriptCallStack* callStack)
424 {
425 #if ENABLE(INSPECTOR)
426     Page* page = this->page();
427     if (!page)
428         return;
429
430     page->inspectorController()->startGroup(JSMessageSource, callStack);
431 #else
432     UNUSED_PARAM(callStack);
433 #endif
434 }
435
436 void Console::groupCollapsed(ScriptCallStack* callStack)
437 {
438 #if ENABLE(INSPECTOR)
439     Page* page = this->page();
440     if (!page)
441         return;
442
443     page->inspectorController()->startGroup(JSMessageSource, callStack, true);
444 #else
445     UNUSED_PARAM(callStack);
446 #endif
447 }
448
449 void Console::groupEnd()
450 {
451 #if ENABLE(INSPECTOR)
452     Page* page = this->page();
453     if (!page)
454         return;
455
456     page->inspectorController()->endGroup(JSMessageSource, 0, String());
457 #endif
458 }
459
460 void Console::warn(ScriptCallStack* callStack)
461 {
462     addMessage(LogMessageType, WarningMessageLevel, callStack);
463 }
464
465 MemoryInfo* Console::memory() const
466 {
467     m_memory = MemoryInfo::create(m_frame);
468     return m_memory.get();
469 }
470
471 static bool printExceptions = false;
472
473 bool Console::shouldPrintExceptions()
474 {
475     return printExceptions;
476 }
477
478 void Console::setShouldPrintExceptions(bool print)
479 {
480     printExceptions = print;
481 }
482
483 Page* Console::page() const
484 {
485     if (!m_frame)
486         return 0;
487     return m_frame->page();
488 }
489
490 } // namespace WebCore