/ runtime / ConsoleClient.cpp
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