ConsoleClient.cpp
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 "JSCJSValueInlines.h" 31 #include "JSGlobalObject.h" 32 #include "ScriptArguments.h" 33 #include "ScriptCallStack.h" 34 #include "ScriptCallStackFactory.h" 35 #include <wtf/Assertions.h> 36 #include <wtf/text/CString.h> 37 #include <wtf/text/StringBuilder.h> 38 #include <wtf/text/WTFString.h> 39 40 using namespace Inspector; 41 42 namespace JSC { 43 44 static void appendURLAndPosition(StringBuilder& builder, const String& url, unsigned lineNumber, unsigned columnNumber) 45 { 46 if (url.isEmpty()) 47 return; 48 49 builder.append(url); 50 51 if (lineNumber > 0) { 52 builder.append(':'); 53 builder.appendNumber(lineNumber); 54 } 55 56 if (columnNumber > 0) { 57 builder.append(':'); 58 builder.appendNumber(columnNumber); 59 } 60 } 61 62 static void appendMessagePrefix(StringBuilder& builder, MessageSource source, MessageType type, MessageLevel level) 63 { 64 String sourceString; 65 switch (source) { 66 case MessageSource::ConsoleAPI: 67 // Default, no need to be more specific. 68 break; 69 case MessageSource::XML: 70 sourceString = "XML"_s; 71 break; 72 case MessageSource::JS: 73 sourceString = "JS"_s; 74 break; 75 case MessageSource::Network: 76 sourceString = "NETWORK"_s; 77 break; 78 case MessageSource::Storage: 79 sourceString = "STORAGE"_s; 80 break; 81 case MessageSource::AppCache: 82 sourceString = "APPCACHE"_s; 83 break; 84 case MessageSource::Rendering: 85 sourceString = "RENDERING"_s; 86 break; 87 case MessageSource::CSS: 88 sourceString = "CSS"_s; 89 break; 90 case MessageSource::Security: 91 sourceString = "SECURITY"_s; 92 break; 93 case MessageSource::ContentBlocker: 94 sourceString = "CONTENTBLOCKER"_s; 95 break; 96 case MessageSource::Media: 97 sourceString = "MEDIA"_s; 98 break; 99 case MessageSource::MediaSource: 100 sourceString = "MEDIASOURCE"_s; 101 break; 102 case MessageSource::WebRTC: 103 sourceString = "WEBRTC"_s; 104 break; 105 case MessageSource::ITPDebug: 106 sourceString = "ITPDEBUG"_s; 107 break; 108 case MessageSource::PrivateClickMeasurement: 109 sourceString = "PRIVATECLICKMEASUREMENT"_s; 110 break; 111 case MessageSource::Other: 112 sourceString = "OTHER"_s; 113 break; 114 } 115 116 String typeString; 117 switch (type) { 118 case MessageType::Log: 119 // Default, no need to be more specific. 120 break; 121 case MessageType::Clear: 122 typeString = "CLEAR"_s; 123 break; 124 case MessageType::Dir: 125 typeString = "DIR"_s; 126 break; 127 case MessageType::DirXML: 128 typeString = "DIRXML"_s; 129 break; 130 case MessageType::Table: 131 typeString = "TABLE"_s; 132 break; 133 case MessageType::Trace: 134 typeString = "TRACE"_s; 135 break; 136 case MessageType::StartGroup: 137 typeString = "STARTGROUP"_s; 138 break; 139 case MessageType::StartGroupCollapsed: 140 typeString = "STARTGROUPCOLLAPSED"_s; 141 break; 142 case MessageType::EndGroup: 143 typeString = "ENDGROUP"_s; 144 break; 145 case MessageType::Assert: 146 typeString = "ASSERT"_s; 147 break; 148 case MessageType::Timing: 149 typeString = "TIMING"_s; 150 break; 151 case MessageType::Profile: 152 typeString = "PROFILE"_s; 153 break; 154 case MessageType::ProfileEnd: 155 typeString = "PROFILEEND"_s; 156 break; 157 case MessageType::Image: 158 typeString = "IMAGE"_s; 159 break; 160 } 161 162 String levelString; 163 switch (level) { 164 case MessageLevel::Log: 165 // Default, no need to be more specific. 166 if (type == MessageType::Log) 167 levelString = "LOG"_s; 168 break; 169 case MessageLevel::Debug: 170 levelString = "DEBUG"_s; 171 break; 172 case MessageLevel::Info: 173 levelString = "INFO"_s; 174 break; 175 case MessageLevel::Warning: 176 levelString = "WARN"_s; 177 break; 178 case MessageLevel::Error: 179 levelString = "ERROR"_s; 180 break; 181 } 182 183 builder.append("CONSOLE"); 184 if (!sourceString.isEmpty()) 185 builder.append(' ', sourceString); 186 if (!typeString.isEmpty()) 187 builder.append(' ', typeString); 188 if (!levelString.isEmpty()) 189 builder.append(' ', levelString); 190 } 191 192 void ConsoleClient::printConsoleMessage(MessageSource source, MessageType type, MessageLevel level, const String& message, const String& url, unsigned lineNumber, unsigned columnNumber) 193 { 194 StringBuilder builder; 195 196 if (!url.isEmpty()) { 197 appendURLAndPosition(builder, url, lineNumber, columnNumber); 198 builder.appendLiteral(": "); 199 } 200 201 appendMessagePrefix(builder, source, type, level); 202 builder.append(' '); 203 builder.append(message); 204 205 WTFLogAlways("%s", builder.toString().utf8().data()); 206 } 207 208 void ConsoleClient::printConsoleMessageWithArguments(MessageSource source, MessageType type, MessageLevel level, JSC::JSGlobalObject* globalObject, Ref<ScriptArguments>&& arguments) 209 { 210 bool isTraceMessage = type == MessageType::Trace; 211 size_t stackSize = isTraceMessage ? ScriptCallStack::maxCallStackSizeToCapture : 1; 212 Ref<ScriptCallStack> callStack = createScriptCallStackForConsole(globalObject, stackSize); 213 const ScriptCallFrame& lastCaller = callStack->at(0); 214 215 StringBuilder builder; 216 217 if (!lastCaller.sourceURL().isEmpty()) { 218 appendURLAndPosition(builder, lastCaller.sourceURL(), lastCaller.lineNumber(), lastCaller.columnNumber()); 219 builder.appendLiteral(": "); 220 } 221 222 appendMessagePrefix(builder, source, type, level); 223 for (size_t i = 0; i < arguments->argumentCount(); ++i) { 224 builder.append(' '); 225 auto* globalObject = arguments->globalObject(); 226 auto scope = DECLARE_CATCH_SCOPE(globalObject->vm()); 227 builder.append(arguments->argumentAt(i).toWTFString(globalObject)); 228 scope.clearException(); 229 } 230 231 WTFLogAlways("%s", builder.toString().utf8().data()); 232 233 if (isTraceMessage) { 234 for (size_t i = 0; i < callStack->size(); ++i) { 235 const ScriptCallFrame& callFrame = callStack->at(i); 236 String functionName = String(callFrame.functionName()); 237 if (functionName.isEmpty()) 238 functionName = "(unknown)"_s; 239 240 StringBuilder callFrameBuilder; 241 callFrameBuilder.appendNumber(i); 242 callFrameBuilder.appendLiteral(": "); 243 callFrameBuilder.append(functionName); 244 callFrameBuilder.append('('); 245 appendURLAndPosition(callFrameBuilder, callFrame.sourceURL(), callFrame.lineNumber(), callFrame.columnNumber()); 246 callFrameBuilder.append(')'); 247 248 WTFLogAlways("%s", callFrameBuilder.toString().utf8().data()); 249 } 250 } 251 } 252 253 void ConsoleClient::internalMessageWithTypeAndLevel(MessageType type, MessageLevel level, JSC::JSGlobalObject* globalObject, Ref<ScriptArguments>&& arguments, ArgumentRequirement argumentRequirement) 254 { 255 if (argumentRequirement == ArgumentRequired && !arguments->argumentCount()) 256 return; 257 258 messageWithTypeAndLevel(type, level, globalObject, WTFMove(arguments)); 259 } 260 261 void ConsoleClient::logWithLevel(JSGlobalObject* globalObject, Ref<ScriptArguments>&& arguments, MessageLevel level) 262 { 263 internalMessageWithTypeAndLevel(MessageType::Log, level, globalObject, WTFMove(arguments), ArgumentRequired); 264 } 265 266 void ConsoleClient::clear(JSGlobalObject* globalObject) 267 { 268 internalMessageWithTypeAndLevel(MessageType::Clear, MessageLevel::Log, globalObject, ScriptArguments::create(globalObject, { }), ArgumentNotRequired); 269 } 270 271 void ConsoleClient::dir(JSGlobalObject* globalObject, Ref<ScriptArguments>&& arguments) 272 { 273 internalMessageWithTypeAndLevel(MessageType::Dir, MessageLevel::Log, globalObject, WTFMove(arguments), ArgumentRequired); 274 } 275 276 void ConsoleClient::dirXML(JSGlobalObject* globalObject, Ref<ScriptArguments>&& arguments) 277 { 278 internalMessageWithTypeAndLevel(MessageType::DirXML, MessageLevel::Log, globalObject, WTFMove(arguments), ArgumentRequired); 279 } 280 281 void ConsoleClient::table(JSGlobalObject* globalObject, Ref<ScriptArguments>&& arguments) 282 { 283 internalMessageWithTypeAndLevel(MessageType::Table, MessageLevel::Log, globalObject, WTFMove(arguments), ArgumentRequired); 284 } 285 286 void ConsoleClient::trace(JSGlobalObject* globalObject, Ref<ScriptArguments>&& arguments) 287 { 288 internalMessageWithTypeAndLevel(MessageType::Trace, MessageLevel::Log, globalObject, WTFMove(arguments), ArgumentNotRequired); 289 } 290 291 void ConsoleClient::assertion(JSGlobalObject* globalObject, Ref<ScriptArguments>&& arguments) 292 { 293 internalMessageWithTypeAndLevel(MessageType::Assert, MessageLevel::Error, globalObject, WTFMove(arguments), ArgumentNotRequired); 294 } 295 296 void ConsoleClient::group(JSGlobalObject* globalObject, Ref<ScriptArguments>&& arguments) 297 { 298 internalMessageWithTypeAndLevel(MessageType::StartGroup, MessageLevel::Log, globalObject, WTFMove(arguments), ArgumentNotRequired); 299 } 300 301 void ConsoleClient::groupCollapsed(JSGlobalObject* globalObject, Ref<ScriptArguments>&& arguments) 302 { 303 internalMessageWithTypeAndLevel(MessageType::StartGroupCollapsed, MessageLevel::Log, globalObject, WTFMove(arguments), ArgumentNotRequired); 304 } 305 306 void ConsoleClient::groupEnd(JSGlobalObject* globalObject, Ref<ScriptArguments>&& arguments) 307 { 308 internalMessageWithTypeAndLevel(MessageType::EndGroup, MessageLevel::Log, globalObject, WTFMove(arguments), ArgumentNotRequired); 309 } 310 311 } // namespace JSC