1/*
2 * Copyright (C) 2013, 2014 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 * 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. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "ConsoleClient.h"
28
29#include "CatchScope.h"
30#include "JSCInlines.h"
31#include "ScriptArguments.h"
32#include "ScriptCallStack.h"
33#include "ScriptCallStackFactory.h"
34#include <wtf/Assertions.h>
35#include <wtf/text/CString.h>
36#include <wtf/text/StringBuilder.h>
37#include <wtf/text/WTFString.h>
38
39using namespace Inspector;
40
41namespace JSC {
42
43static void appendURLAndPosition(StringBuilder& builder, const String& url, unsigned lineNumber, unsigned columnNumber)
44{
45 if (url.isEmpty())
46 return;
47
48 builder.append(url);
49
50 if (lineNumber > 0) {
51 builder.append(':');
52 builder.appendNumber(lineNumber);
53 }
54
55 if (columnNumber > 0) {
56 builder.append(':');
57 builder.appendNumber(columnNumber);
58 }
59}
60
61static void appendMessagePrefix(StringBuilder& builder, MessageSource source, MessageType type, MessageLevel level)
62{
63 const char* sourceString;
64 switch (source) {
65 case MessageSource::XML:
66 sourceString = "XML";
67 break;
68 case MessageSource::JS:
69 sourceString = "JS";
70 break;
71 case MessageSource::Network:
72 sourceString = "NETWORK";
73 break;
74 case MessageSource::ConsoleAPI:
75 sourceString = "CONSOLE";
76 break;
77 case MessageSource::Storage:
78 sourceString = "STORAGE";
79 break;
80 case MessageSource::AppCache:
81 sourceString = "APPCACHE";
82 break;
83 case MessageSource::Rendering:
84 sourceString = "RENDERING";
85 break;
86 case MessageSource::CSS:
87 sourceString = "CSS";
88 break;
89 case MessageSource::Security:
90 sourceString = "SECURITY";
91 break;
92 case MessageSource::Other:
93 sourceString = "OTHER";
94 break;
95 default:
96 ASSERT_NOT_REACHED();
97 sourceString = "UNKNOWN";
98 break;
99 }
100
101 const char* levelString;
102 switch (level) {
103 case MessageLevel::Debug:
104 levelString = "DEBUG";
105 break;
106 case MessageLevel::Log:
107 levelString = "LOG";
108 break;
109 case MessageLevel::Info:
110 levelString = "INFO";
111 break;
112 case MessageLevel::Warning:
113 levelString = "WARN";
114 break;
115 case MessageLevel::Error:
116 levelString = "ERROR";
117 break;
118 default:
119 ASSERT_NOT_REACHED();
120 levelString = "UNKNOWN";
121 break;
122 }
123
124 if (type == MessageType::Trace)
125 levelString = "TRACE";
126 else if (type == MessageType::Table)
127 levelString = "TABLE";
128
129 builder.append(sourceString);
130 builder.append(' ');
131 builder.append(levelString);
132}
133
134void ConsoleClient::printConsoleMessage(MessageSource source, MessageType type, MessageLevel level, const String& message, const String& url, unsigned lineNumber, unsigned columnNumber)
135{
136 StringBuilder builder;
137
138 if (!url.isEmpty()) {
139 appendURLAndPosition(builder, url, lineNumber, columnNumber);
140 builder.appendLiteral(": ");
141 }
142
143 appendMessagePrefix(builder, source, type, level);
144 builder.append(' ');
145 builder.append(message);
146
147 WTFLogAlways("%s", builder.toString().utf8().data());
148}
149
150void ConsoleClient::printConsoleMessageWithArguments(MessageSource source, MessageType type, MessageLevel level, JSC::ExecState* exec, Ref<ScriptArguments>&& arguments)
151{
152 bool isTraceMessage = type == MessageType::Trace;
153 size_t stackSize = isTraceMessage ? ScriptCallStack::maxCallStackSizeToCapture : 1;
154 Ref<ScriptCallStack> callStack = createScriptCallStackForConsole(exec, stackSize);
155 const ScriptCallFrame& lastCaller = callStack->at(0);
156
157 StringBuilder builder;
158
159 if (!lastCaller.sourceURL().isEmpty()) {
160 appendURLAndPosition(builder, lastCaller.sourceURL(), lastCaller.lineNumber(), lastCaller.columnNumber());
161 builder.appendLiteral(": ");
162 }
163
164 appendMessagePrefix(builder, source, type, level);
165 for (size_t i = 0; i < arguments->argumentCount(); ++i) {
166 builder.append(' ');
167 auto* state = arguments->globalState();
168 auto scope = DECLARE_CATCH_SCOPE(state->vm());
169 builder.append(arguments->argumentAt(i).toWTFString(state));
170 scope.clearException();
171 }
172
173 WTFLogAlways("%s", builder.toString().utf8().data());
174
175 if (isTraceMessage) {
176 for (size_t i = 0; i < callStack->size(); ++i) {
177 const ScriptCallFrame& callFrame = callStack->at(i);
178 String functionName = String(callFrame.functionName());
179 if (functionName.isEmpty())
180 functionName = "(unknown)"_s;
181
182 StringBuilder callFrameBuilder;
183 callFrameBuilder.appendNumber(i);
184 callFrameBuilder.appendLiteral(": ");
185 callFrameBuilder.append(functionName);
186 callFrameBuilder.append('(');
187 appendURLAndPosition(callFrameBuilder, callFrame.sourceURL(), callFrame.lineNumber(), callFrame.columnNumber());
188 callFrameBuilder.append(')');
189
190 WTFLogAlways("%s", callFrameBuilder.toString().utf8().data());
191 }
192 }
193}
194
195void ConsoleClient::internalMessageWithTypeAndLevel(MessageType type, MessageLevel level, JSC::ExecState* exec, Ref<ScriptArguments>&& arguments, ArgumentRequirement argumentRequirement)
196{
197 if (argumentRequirement == ArgumentRequired && !arguments->argumentCount())
198 return;
199
200 messageWithTypeAndLevel(type, level, exec, WTFMove(arguments));
201}
202
203void ConsoleClient::logWithLevel(ExecState* exec, Ref<ScriptArguments>&& arguments, MessageLevel level)
204{
205 internalMessageWithTypeAndLevel(MessageType::Log, level, exec, WTFMove(arguments), ArgumentRequired);
206}
207
208void ConsoleClient::clear(ExecState* exec)
209{
210 internalMessageWithTypeAndLevel(MessageType::Clear, MessageLevel::Log, exec, ScriptArguments::create(*exec, { }), ArgumentNotRequired);
211}
212
213void ConsoleClient::dir(ExecState* exec, Ref<ScriptArguments>&& arguments)
214{
215 internalMessageWithTypeAndLevel(MessageType::Dir, MessageLevel::Log, exec, WTFMove(arguments), ArgumentRequired);
216}
217
218void ConsoleClient::dirXML(ExecState* exec, Ref<ScriptArguments>&& arguments)
219{
220 internalMessageWithTypeAndLevel(MessageType::DirXML, MessageLevel::Log, exec, WTFMove(arguments), ArgumentRequired);
221}
222
223void ConsoleClient::table(ExecState* exec, Ref<ScriptArguments>&& arguments)
224{
225 internalMessageWithTypeAndLevel(MessageType::Table, MessageLevel::Log, exec, WTFMove(arguments), ArgumentRequired);
226}
227
228void ConsoleClient::trace(ExecState* exec, Ref<ScriptArguments>&& arguments)
229{
230 internalMessageWithTypeAndLevel(MessageType::Trace, MessageLevel::Log, exec, WTFMove(arguments), ArgumentNotRequired);
231}
232
233void ConsoleClient::assertion(ExecState* exec, Ref<ScriptArguments>&& arguments)
234{
235 internalMessageWithTypeAndLevel(MessageType::Assert, MessageLevel::Error, exec, WTFMove(arguments), ArgumentNotRequired);
236}
237
238void ConsoleClient::group(ExecState* exec, Ref<ScriptArguments>&& arguments)
239{
240 internalMessageWithTypeAndLevel(MessageType::StartGroup, MessageLevel::Log, exec, WTFMove(arguments), ArgumentNotRequired);
241}
242
243void ConsoleClient::groupCollapsed(ExecState* exec, Ref<ScriptArguments>&& arguments)
244{
245 internalMessageWithTypeAndLevel(MessageType::StartGroupCollapsed, MessageLevel::Log, exec, WTFMove(arguments), ArgumentNotRequired);
246}
247
248void ConsoleClient::groupEnd(ExecState* exec, Ref<ScriptArguments>&& arguments)
249{
250 internalMessageWithTypeAndLevel(MessageType::EndGroup, MessageLevel::Log, exec, WTFMove(arguments), ArgumentNotRequired);
251}
252
253} // namespace JSC
254