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 "ConsoleAPITypes.h"
35#include "ConsoleTypes.h"
36#include "Document.h"
37#include "Frame.h"
38#include "FrameLoader.h"
39#include "FrameTree.h"
40#include "InspectorConsoleInstrumentation.h"
41#include "InspectorController.h"
42#include "Page.h"
43#include "PageConsole.h"
44#include "PageGroup.h"
45#include "ScriptArguments.h"
46#include "ScriptCallStack.h"
47#include "ScriptCallStackFactory.h"
48#include "ScriptProfile.h"
49#include "ScriptProfiler.h"
50#include "ScriptValue.h"
51#include "ScriptableDocumentParser.h"
52#include "Settings.h"
53#include <stdio.h>
54#include <wtf/text/CString.h>
55#include <wtf/text/WTFString.h>
56
57namespace WebCore {
58
59Console::Console(Frame* frame)
60    : DOMWindowProperty(frame)
61{
62}
63
64Console::~Console()
65{
66}
67
68static void internalAddMessage(Page* page, MessageType type, MessageLevel level, ScriptState* state, PassRefPtr<ScriptArguments> prpArguments, bool acceptNoArguments = false, bool printTrace = false)
69{
70    RefPtr<ScriptArguments> arguments = prpArguments;
71
72    if (!page)
73        return;
74
75    if (!acceptNoArguments && !arguments->argumentCount())
76        return;
77
78    size_t stackSize = printTrace ? ScriptCallStack::maxCallStackSizeToCapture : 1;
79    RefPtr<ScriptCallStack> callStack(createScriptCallStack(state, stackSize));
80    const ScriptCallFrame& lastCaller = callStack->at(0);
81
82    String message;
83    bool gotMessage = arguments->getFirstArgumentAsString(message);
84    InspectorInstrumentation::addMessageToConsole(page, ConsoleAPIMessageSource, type, level, message, state, arguments);
85
86    if (page->settings()->privateBrowsingEnabled())
87        return;
88
89    if (gotMessage)
90        page->chrome().client()->addMessageToConsole(ConsoleAPIMessageSource, type, level, message, lastCaller.lineNumber(), lastCaller.columnNumber(), lastCaller.sourceURL());
91
92    if (!page->settings()->logsPageMessagesToSystemConsoleEnabled() && !PageConsole::shouldPrintExceptions())
93        return;
94
95    PageConsole::printSourceURLAndLine(lastCaller.sourceURL(), lastCaller.lineNumber());
96    PageConsole::printMessageSourceAndLevelPrefix(ConsoleAPIMessageSource, level);
97
98    for (size_t i = 0; i < arguments->argumentCount(); ++i) {
99        String argAsString = arguments->argumentAt(i).toString(arguments->globalState());
100        printf(" %s", argAsString.utf8().data());
101    }
102
103    printf("\n");
104
105    if (printTrace) {
106        printf("Stack Trace\n");
107        for (size_t i = 0; i < callStack->size(); ++i) {
108            String functionName = String(callStack->at(i).functionName());
109            printf("\t%s\n", functionName.utf8().data());
110        }
111    }
112}
113
114void Console::debug(ScriptState* state, PassRefPtr<ScriptArguments> arguments)
115{
116    internalAddMessage(page(), LogMessageType, DebugMessageLevel, state, arguments);
117}
118
119void Console::error(ScriptState* state, PassRefPtr<ScriptArguments> arguments)
120{
121    internalAddMessage(page(), LogMessageType, ErrorMessageLevel, state, arguments);
122}
123
124void Console::info(ScriptState* state, PassRefPtr<ScriptArguments> arguments)
125{
126    log(state, arguments);
127}
128
129void Console::log(ScriptState* state, PassRefPtr<ScriptArguments> arguments)
130{
131    internalAddMessage(page(), LogMessageType, LogMessageLevel, state, arguments);
132}
133
134void Console::warn(ScriptState* state, PassRefPtr<ScriptArguments> arguments)
135{
136    internalAddMessage(page(), LogMessageType, WarningMessageLevel, state, arguments);
137}
138
139void Console::dir(ScriptState* state, PassRefPtr<ScriptArguments> arguments)
140{
141    internalAddMessage(page(), DirMessageType, LogMessageLevel, state, arguments);
142}
143
144void Console::dirxml(ScriptState* state, PassRefPtr<ScriptArguments> arguments)
145{
146    internalAddMessage(page(), DirXMLMessageType, LogMessageLevel, state, arguments);
147}
148
149void Console::table(ScriptState* state, PassRefPtr<ScriptArguments> arguments)
150{
151    internalAddMessage(page(), TableMessageType, LogMessageLevel, state, arguments);
152}
153
154void Console::clear(ScriptState* state, PassRefPtr<ScriptArguments> arguments)
155{
156    internalAddMessage(page(), ClearMessageType, LogMessageLevel, state, arguments, true);
157}
158
159void Console::trace(ScriptState* state, PassRefPtr<ScriptArguments> arguments)
160{
161    internalAddMessage(page(), TraceMessageType, LogMessageLevel, state, arguments, true, true);
162}
163
164void Console::assertCondition(ScriptState* state, PassRefPtr<ScriptArguments> arguments, bool condition)
165{
166    if (condition)
167        return;
168
169    internalAddMessage(page(), AssertMessageType, ErrorMessageLevel, state, arguments, true);
170}
171
172void Console::count(ScriptState* state, PassRefPtr<ScriptArguments> arguments)
173{
174    InspectorInstrumentation::consoleCount(page(), state, arguments);
175}
176
177#if ENABLE(JAVASCRIPT_DEBUGGER)
178
179void Console::profile(const String& title, ScriptState* state)
180{
181    Page* page = this->page();
182    if (!page)
183        return;
184
185    // FIXME: log a console message when profiling is disabled.
186    if (!InspectorInstrumentation::profilerEnabled(page))
187        return;
188
189    String resolvedTitle = title;
190    if (title.isNull()) // no title so give it the next user initiated profile title.
191        resolvedTitle = InspectorInstrumentation::getCurrentUserInitiatedProfileName(page, true);
192
193    ScriptProfiler::start(state, resolvedTitle);
194
195    RefPtr<ScriptCallStack> callStack(createScriptCallStack(state, 1));
196    const ScriptCallFrame& lastCaller = callStack->at(0);
197    InspectorInstrumentation::addStartProfilingMessageToConsole(page, resolvedTitle, lastCaller.lineNumber(), lastCaller.columnNumber(), lastCaller.sourceURL());
198}
199
200void Console::profileEnd(const String& title, ScriptState* state)
201{
202    Page* page = this->page();
203    if (!page)
204        return;
205
206    if (!InspectorInstrumentation::profilerEnabled(page))
207        return;
208
209    RefPtr<ScriptProfile> profile = ScriptProfiler::stop(state, title);
210    if (!profile)
211        return;
212
213    m_profiles.append(profile);
214    RefPtr<ScriptCallStack> callStack(createScriptCallStack(state, 1));
215    InspectorInstrumentation::addProfile(page, profile, callStack);
216}
217
218#endif
219
220void Console::time(const String& title)
221{
222    InspectorInstrumentation::startConsoleTiming(m_frame, title);
223}
224
225void Console::timeEnd(ScriptState* state, const String& title)
226{
227    RefPtr<ScriptCallStack> callStack(createScriptCallStackForConsole(state));
228    InspectorInstrumentation::stopConsoleTiming(m_frame, title, callStack.release());
229}
230
231void Console::timeStamp(PassRefPtr<ScriptArguments> arguments)
232{
233    InspectorInstrumentation::consoleTimeStamp(m_frame, arguments);
234}
235
236void Console::group(ScriptState* state, PassRefPtr<ScriptArguments> arguments)
237{
238    InspectorInstrumentation::addMessageToConsole(page(), ConsoleAPIMessageSource, StartGroupMessageType, LogMessageLevel, String(), state, arguments);
239}
240
241void Console::groupCollapsed(ScriptState* state, PassRefPtr<ScriptArguments> arguments)
242{
243    InspectorInstrumentation::addMessageToConsole(page(), ConsoleAPIMessageSource, StartGroupCollapsedMessageType, LogMessageLevel, String(), state, arguments);
244}
245
246void Console::groupEnd()
247{
248    InspectorInstrumentation::addMessageToConsole(page(), ConsoleAPIMessageSource, EndGroupMessageType, LogMessageLevel, String(), String(), 0, 0);
249}
250
251Page* Console::page() const
252{
253    if (!m_frame)
254        return 0;
255    return m_frame->page();
256}
257
258} // namespace WebCore
259