1/* 2 * Copyright (C) 2014 Apple Inc. All rights reserved. 3 * Copyright (C) 2011 Google Inc. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 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 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY 18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "InspectorConsoleAgent.h" 28 29#if ENABLE(INSPECTOR) 30 31#include "ConsoleMessage.h" 32#include "InjectedScriptManager.h" 33#include "ScriptArguments.h" 34#include "ScriptCallFrame.h" 35#include "ScriptCallStack.h" 36#include "ScriptCallStackFactory.h" 37#include "ScriptObject.h" 38#include <wtf/CurrentTime.h> 39#include <wtf/text/StringBuilder.h> 40#include <wtf/text/WTFString.h> 41 42namespace Inspector { 43 44static const unsigned maximumConsoleMessages = 1000; 45static const int expireConsoleMessagesStep = 100; 46 47InspectorConsoleAgent::InspectorConsoleAgent(InjectedScriptManager* injectedScriptManager) 48 : InspectorAgentBase(ASCIILiteral("Console")) 49 , m_injectedScriptManager(injectedScriptManager) 50 , m_previousMessage(nullptr) 51 , m_expiredConsoleMessageCount(0) 52 , m_enabled(false) 53{ 54} 55 56InspectorConsoleAgent::~InspectorConsoleAgent() 57{ 58} 59 60void InspectorConsoleAgent::didCreateFrontendAndBackend(Inspector::InspectorFrontendChannel* frontendChannel, InspectorBackendDispatcher* backendDispatcher) 61{ 62 m_frontendDispatcher = std::make_unique<InspectorConsoleFrontendDispatcher>(frontendChannel); 63 m_backendDispatcher = InspectorConsoleBackendDispatcher::create(backendDispatcher, this); 64} 65 66void InspectorConsoleAgent::willDestroyFrontendAndBackend(InspectorDisconnectReason) 67{ 68 m_frontendDispatcher = nullptr; 69 m_backendDispatcher.clear(); 70 71 String errorString; 72 disable(&errorString); 73} 74 75void InspectorConsoleAgent::enable(ErrorString*) 76{ 77 if (m_enabled) 78 return; 79 80 m_enabled = true; 81 82 if (m_expiredConsoleMessageCount) { 83 ConsoleMessage expiredMessage(MessageSource::Other, MessageType::Log, MessageLevel::Warning, String::format("%d console messages are not shown.", m_expiredConsoleMessageCount)); 84 expiredMessage.addToFrontend(m_frontendDispatcher.get(), m_injectedScriptManager, false); 85 } 86 87 size_t messageCount = m_consoleMessages.size(); 88 for (size_t i = 0; i < messageCount; ++i) 89 m_consoleMessages[i]->addToFrontend(m_frontendDispatcher.get(), m_injectedScriptManager, false); 90} 91 92void InspectorConsoleAgent::disable(ErrorString*) 93{ 94 if (!m_enabled) 95 return; 96 97 m_enabled = false; 98} 99 100void InspectorConsoleAgent::clearMessages(ErrorString*) 101{ 102 m_consoleMessages.clear(); 103 m_expiredConsoleMessageCount = 0; 104 m_previousMessage = nullptr; 105 106 m_injectedScriptManager->releaseObjectGroup(ASCIILiteral("console")); 107 108 if (m_frontendDispatcher && m_enabled) 109 m_frontendDispatcher->messagesCleared(); 110} 111 112void InspectorConsoleAgent::reset() 113{ 114 ErrorString error; 115 clearMessages(&error); 116 117 m_times.clear(); 118 m_counts.clear(); 119} 120 121void InspectorConsoleAgent::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, const String& message, PassRefPtr<ScriptCallStack> callStack, unsigned long requestIdentifier) 122{ 123 if (!m_injectedScriptManager->inspectorEnvironment().developerExtrasEnabled()) 124 return; 125 126 if (type == MessageType::Clear) { 127 ErrorString error; 128 clearMessages(&error); 129 } 130 131 addConsoleMessage(std::make_unique<ConsoleMessage>(source, type, level, message, callStack, requestIdentifier)); 132} 133 134void InspectorConsoleAgent::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, const String& message, JSC::ExecState* state, PassRefPtr<ScriptArguments> arguments, unsigned long requestIdentifier) 135{ 136 if (!m_injectedScriptManager->inspectorEnvironment().developerExtrasEnabled()) 137 return; 138 139 if (type == MessageType::Clear) { 140 ErrorString error; 141 clearMessages(&error); 142 } 143 144 addConsoleMessage(std::make_unique<ConsoleMessage>(source, type, level, message, arguments, state, requestIdentifier)); 145} 146 147void InspectorConsoleAgent::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, const String& message, const String& scriptID, unsigned lineNumber, unsigned columnNumber, JSC::ExecState* state, unsigned long requestIdentifier) 148{ 149 if (!m_injectedScriptManager->inspectorEnvironment().developerExtrasEnabled()) 150 return; 151 152 if (type == MessageType::Clear) { 153 ErrorString error; 154 clearMessages(&error); 155 } 156 157 addConsoleMessage(std::make_unique<ConsoleMessage>(source, type, level, message, scriptID, lineNumber, columnNumber, state, requestIdentifier)); 158} 159 160Vector<unsigned> InspectorConsoleAgent::consoleMessageArgumentCounts() const 161{ 162 Vector<unsigned> result(m_consoleMessages.size()); 163 for (size_t i = 0; i < m_consoleMessages.size(); i++) 164 result[i] = m_consoleMessages[i]->argumentCount(); 165 return result; 166} 167 168void InspectorConsoleAgent::startTiming(const String& title) 169{ 170 // Follow Firebug's behavior of requiring a title that is not null or 171 // undefined for timing functions 172 if (title.isNull()) 173 return; 174 175 m_times.add(title, monotonicallyIncreasingTime()); 176} 177 178void InspectorConsoleAgent::stopTiming(const String& title, PassRefPtr<ScriptCallStack> callStack) 179{ 180 // Follow Firebug's behavior of requiring a title that is not null or 181 // undefined for timing functions 182 if (title.isNull()) 183 return; 184 185 HashMap<String, double>::iterator it = m_times.find(title); 186 if (it == m_times.end()) 187 return; 188 189 double startTime = it->value; 190 m_times.remove(it); 191 192 double elapsed = monotonicallyIncreasingTime() - startTime; 193 String message = title + String::format(": %.3fms", elapsed * 1000); 194 addMessageToConsole(MessageSource::ConsoleAPI, MessageType::Timing, MessageLevel::Debug, message, callStack); 195} 196 197void InspectorConsoleAgent::count(JSC::ExecState* state, PassRefPtr<ScriptArguments> arguments) 198{ 199 RefPtr<ScriptCallStack> callStack(createScriptCallStackForConsole(state, ScriptCallStack::maxCallStackSizeToCapture)); 200 const ScriptCallFrame& lastCaller = callStack->at(0); 201 // Follow Firebug's behavior of counting with null and undefined title in 202 // the same bucket as no argument 203 String title; 204 arguments->getFirstArgumentAsString(title); 205 String identifier = title + '@' + lastCaller.sourceURL() + ':' + String::number(lastCaller.lineNumber()); 206 207 HashMap<String, unsigned>::iterator it = m_counts.find(identifier); 208 int count; 209 if (it == m_counts.end()) 210 count = 1; 211 else { 212 count = it->value + 1; 213 m_counts.remove(it); 214 } 215 216 m_counts.add(identifier, count); 217 218 String message = title + ": " + String::number(count); 219 addMessageToConsole(MessageSource::ConsoleAPI, MessageType::Log, MessageLevel::Debug, message, callStack); 220} 221 222static bool isGroupMessage(MessageType type) 223{ 224 return type == MessageType::StartGroup 225 || type == MessageType::StartGroupCollapsed 226 || type == MessageType::EndGroup; 227} 228 229void InspectorConsoleAgent::addConsoleMessage(std::unique_ptr<ConsoleMessage> consoleMessage) 230{ 231 ASSERT(m_injectedScriptManager->inspectorEnvironment().developerExtrasEnabled()); 232 ASSERT_ARG(consoleMessage, consoleMessage); 233 234 if (m_previousMessage && !isGroupMessage(m_previousMessage->type()) && m_previousMessage->isEqual(consoleMessage.get())) { 235 m_previousMessage->incrementCount(); 236 if (m_frontendDispatcher && m_enabled) 237 m_previousMessage->updateRepeatCountInConsole(m_frontendDispatcher.get()); 238 } else { 239 m_previousMessage = consoleMessage.get(); 240 m_consoleMessages.append(WTF::move(consoleMessage)); 241 if (m_frontendDispatcher && m_enabled) 242 m_previousMessage->addToFrontend(m_frontendDispatcher.get(), m_injectedScriptManager, true); 243 } 244 245 if (!m_frontendDispatcher && m_consoleMessages.size() >= maximumConsoleMessages) { 246 m_expiredConsoleMessageCount += expireConsoleMessagesStep; 247 m_consoleMessages.remove(0, expireConsoleMessagesStep); 248 } 249} 250 251} // namespace Inspector 252 253#endif // ENABLE(INSPECTOR) 254