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 "Frame.h" 35 #include "FrameLoader.h" 36 #include "FrameTree.h" 37 #include "InspectorConsoleInstrumentation.h" 38 #include "InspectorController.h" 39 #include "MemoryInfo.h" 40 #include "Page.h" 41 #include "PageGroup.h" 42 #include "PlatformString.h" 43 44 #include "ScriptArguments.h" 45 #include "ScriptCallStack.h" 46 #include "ScriptProfile.h" 47 #include "ScriptProfiler.h" 48 #include <stdio.h> 49 #include <wtf/text/CString.h> 50 #include <wtf/UnusedParam.h> 51 52 namespace WebCore { 53 54 Console::Console(Frame* frame) 55 : m_frame(frame) 56 { 57 } 58 59 Frame* Console::frame() const 60 { 61 return m_frame; 62 } 63 64 void Console::disconnectFrame() 65 { 66 if (m_memory) 67 m_memory = 0; 68 m_frame = 0; 69 } 70 71 static void printSourceURLAndLine(const String& sourceURL, unsigned lineNumber) 72 { 73 if (!sourceURL.isEmpty()) { 74 if (lineNumber > 0) 75 printf("%s:%d: ", sourceURL.utf8().data(), lineNumber); 76 else 77 printf("%s: ", sourceURL.utf8().data()); 78 } 79 } 80 81 static void printMessageSourceAndLevelPrefix(MessageSource source, MessageLevel level) 82 { 83 const char* sourceString; 84 switch (source) { 85 case HTMLMessageSource: 86 sourceString = "HTML"; 87 break; 88 case WMLMessageSource: 89 sourceString = "WML"; 90 break; 91 case XMLMessageSource: 92 sourceString = "XML"; 93 break; 94 case JSMessageSource: 95 sourceString = "JS"; 96 break; 97 case CSSMessageSource: 98 sourceString = "CSS"; 99 break; 100 case OtherMessageSource: 101 sourceString = "OTHER"; 102 break; 103 default: 104 ASSERT_NOT_REACHED(); 105 sourceString = "UNKNOWN"; 106 break; 107 } 108 109 const char* levelString; 110 switch (level) { 111 case TipMessageLevel: 112 levelString = "TIP"; 113 break; 114 case LogMessageLevel: 115 levelString = "LOG"; 116 break; 117 case WarningMessageLevel: 118 levelString = "WARN"; 119 break; 120 case ErrorMessageLevel: 121 levelString = "ERROR"; 122 break; 123 case DebugMessageLevel: 124 levelString = "DEBUG"; 125 break; 126 default: 127 ASSERT_NOT_REACHED(); 128 levelString = "UNKNOWN"; 129 break; 130 } 131 132 printf("%s %s:", sourceString, levelString); 133 } 134 135 void Console::addMessage(MessageSource source, MessageType type, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceURL) 136 { 137 addMessage(source, type, level, message, lineNumber, sourceURL, 0); 138 } 139 140 void Console::addMessage(MessageSource source, MessageType type, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceURL, PassRefPtr<ScriptCallStack> callStack) 141 { 142 Page* page = this->page(); 143 if (!page) 144 return; 145 146 page->chrome()->client()->addMessageToConsole(source, type, level, message, lineNumber, sourceURL); 147 148 if (callStack) 149 InspectorInstrumentation::addMessageToConsole(page, source, type, level, message, 0, callStack); 150 else 151 InspectorInstrumentation::addMessageToConsole(page, source, type, level, message, lineNumber, sourceURL); 152 153 if (!Console::shouldPrintExceptions()) 154 return; 155 156 printSourceURLAndLine(sourceURL, lineNumber); 157 printMessageSourceAndLevelPrefix(source, level); 158 159 printf(" %s\n", message.utf8().data()); 160 } 161 162 void Console::addMessage(MessageType type, MessageLevel level, PassRefPtr<ScriptArguments> prpArguments, PassRefPtr<ScriptCallStack> prpCallStack, bool acceptNoArguments) 163 { 164 RefPtr<ScriptArguments> arguments = prpArguments; 165 RefPtr<ScriptCallStack> callStack = prpCallStack; 166 167 Page* page = this->page(); 168 if (!page) 169 return; 170 171 const ScriptCallFrame& lastCaller = callStack->at(0); 172 173 if (!acceptNoArguments && !arguments->argumentCount()) 174 return; 175 176 if (Console::shouldPrintExceptions()) { 177 printSourceURLAndLine(lastCaller.sourceURL(), 0); 178 printMessageSourceAndLevelPrefix(JSMessageSource, level); 179 180 for (unsigned i = 0; i < arguments->argumentCount(); ++i) { 181 String argAsString; 182 if (arguments->argumentAt(i).getString(arguments->globalState(), argAsString)) 183 printf(" %s", argAsString.utf8().data()); 184 } 185 printf("\n"); 186 } 187 188 String message; 189 if (arguments->getFirstArgumentAsString(message)) 190 page->chrome()->client()->addMessageToConsole(JSMessageSource, type, level, message, lastCaller.lineNumber(), lastCaller.sourceURL()); 191 192 InspectorInstrumentation::addMessageToConsole(page, JSMessageSource, type, level, message, arguments, callStack); 193 } 194 195 void Console::debug(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack) 196 { 197 // In Firebug, console.debug has the same behavior as console.log. So we'll do the same. 198 log(arguments, callStack); 199 } 200 201 void Console::error(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack) 202 { 203 addMessage(LogMessageType, ErrorMessageLevel, arguments, callStack); 204 } 205 206 void Console::info(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack) 207 { 208 log(arguments, callStack); 209 } 210 211 void Console::log(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack) 212 { 213 addMessage(LogMessageType, LogMessageLevel, arguments, callStack); 214 } 215 216 void Console::dir(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack) 217 { 218 addMessage(ObjectMessageType, LogMessageLevel, arguments, callStack); 219 } 220 221 void Console::dirxml(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack) 222 { 223 // The standard behavior of our console.log will print the DOM tree for nodes. 224 log(arguments, callStack); 225 } 226 227 void Console::trace(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> prpCallStack) 228 { 229 RefPtr<ScriptCallStack> callStack = prpCallStack; 230 addMessage(TraceMessageType, LogMessageLevel, arguments, callStack, true); 231 232 if (!shouldPrintExceptions()) 233 return; 234 235 printf("Stack Trace\n"); 236 for (unsigned i = 0; i < callStack->size(); ++i) { 237 String functionName = String(callStack->at(i).functionName()); 238 printf("\t%s\n", functionName.utf8().data()); 239 } 240 } 241 242 void Console::assertCondition(bool condition, PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack) 243 { 244 if (condition) 245 return; 246 247 addMessage(AssertMessageType, ErrorMessageLevel, arguments, callStack, true); 248 } 249 250 void Console::count(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack) 251 { 252 InspectorInstrumentation::consoleCount(page(), arguments, callStack); 253 } 254 255 void Console::markTimeline(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack>) 256 { 257 InspectorInstrumentation::consoleMarkTimeline(page(), arguments); 258 } 259 260 #if ENABLE(JAVASCRIPT_DEBUGGER) 261 262 void Console::profile(const String& title, ScriptState* state, PassRefPtr<ScriptCallStack> callStack) 263 { 264 Page* page = this->page(); 265 if (!page) 266 return; 267 268 // FIXME: log a console message when profiling is disabled. 269 if (!InspectorInstrumentation::profilerEnabled(page)) 270 return; 271 272 String resolvedTitle = title; 273 if (title.isNull()) // no title so give it the next user initiated profile title. 274 resolvedTitle = InspectorInstrumentation::getCurrentUserInitiatedProfileName(page, true); 275 276 ScriptProfiler::start(state, resolvedTitle); 277 278 const ScriptCallFrame& lastCaller = callStack->at(0); 279 InspectorInstrumentation::addStartProfilingMessageToConsole(page, resolvedTitle, lastCaller.lineNumber(), lastCaller.sourceURL()); 280 } 281 282 void Console::profileEnd(const String& title, ScriptState* state, PassRefPtr<ScriptCallStack> callStack) 283 { 284 Page* page = this->page(); 285 if (!page) 286 return; 287 288 if (!InspectorInstrumentation::profilerEnabled(page)) 289 return; 290 291 RefPtr<ScriptProfile> profile = ScriptProfiler::stop(state, title); 292 if (!profile) 293 return; 294 295 m_profiles.append(profile); 296 InspectorInstrumentation::addProfile(page, profile, callStack); 297 } 298 299 #endif 300 301 void Console::time(const String& title) 302 { 303 InspectorInstrumentation::startConsoleTiming(page(), title); 304 } 305 306 void Console::timeEnd(const String& title, PassRefPtr<ScriptArguments>, PassRefPtr<ScriptCallStack> callStack) 307 { 308 InspectorInstrumentation::stopConsoleTiming(page(), title, callStack); 309 } 310 311 void Console::group(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack) 312 { 313 InspectorInstrumentation::addMessageToConsole(page(), JSMessageSource, StartGroupMessageType, LogMessageLevel, String(), arguments, callStack); 314 } 315 316 void Console::groupCollapsed(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack) 317 { 318 InspectorInstrumentation::addMessageToConsole(page(), JSMessageSource, StartGroupCollapsedMessageType, LogMessageLevel, String(), arguments, callStack); 319 } 320 321 void Console::groupEnd() 322 { 323 InspectorInstrumentation::addMessageToConsole(page(), JSMessageSource, EndGroupMessageType, LogMessageLevel, String(), 0, String()); 324 } 325 326 bool Console::shouldCaptureFullStackTrace() const 327 { 328 #if ENABLE(INSPECTOR) 329 Page* page = this->page(); 330 if (!page) 331 return false; 332 333 return page->inspectorController()->hasFrontend(); 334 #else 335 return false; 336 #endif 337 } 338 339 void Console::warn(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack) 340 { 341 addMessage(LogMessageType, WarningMessageLevel, arguments, callStack); 342 } 343 344 MemoryInfo* Console::memory() const 345 { 346 m_memory = MemoryInfo::create(m_frame); 347 return m_memory.get(); 348 } 349 350 static bool printExceptions = false; 351 352 bool Console::shouldPrintExceptions() 353 { 354 return printExceptions; 355 } 356 357 void Console::setShouldPrintExceptions(bool print) 358 { 359 printExceptions = print; 360 } 361 362 Page* Console::page() const 363 { 364 if (!m_frame) 365 return 0; 366 return m_frame->page(); 367 } 368 369 } // namespace WebCore 370