Web Inspector: InspectorController should support multiple frontend channels
[WebKit-https.git] / Source / JavaScriptCore / inspector / agents / InspectorConsoleAgent.cpp
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 #include "ConsoleMessage.h"
30 #include "InjectedScriptManager.h"
31 #include "InspectorFrontendRouter.h"
32 #include "ScriptArguments.h"
33 #include "ScriptCallFrame.h"
34 #include "ScriptCallStack.h"
35 #include "ScriptCallStackFactory.h"
36 #include "ScriptObject.h"
37 #include <wtf/CurrentTime.h>
38 #include <wtf/text/StringBuilder.h>
39 #include <wtf/text/WTFString.h>
40
41 namespace Inspector {
42
43 static const unsigned maximumConsoleMessages = 1000;
44 static const int expireConsoleMessagesStep = 100;
45
46 InspectorConsoleAgent::InspectorConsoleAgent(InjectedScriptManager& injectedScriptManager)
47     : InspectorAgentBase(ASCIILiteral("Console"))
48     , m_injectedScriptManager(injectedScriptManager)
49 {
50 }
51
52 InspectorConsoleAgent::~InspectorConsoleAgent()
53 {
54 }
55
56 void InspectorConsoleAgent::didCreateFrontendAndBackend(FrontendChannel* frontendChannel, BackendDispatcher* backendDispatcher)
57 {
58     m_frontendDispatcher = std::make_unique<ConsoleFrontendDispatcher>(frontendChannel);
59     m_backendDispatcher = ConsoleBackendDispatcher::create(backendDispatcher, this);
60 }
61
62 void InspectorConsoleAgent::willDestroyFrontendAndBackend(DisconnectReason)
63 {
64     m_frontendDispatcher = nullptr;
65     m_backendDispatcher = nullptr;
66
67     String errorString;
68     disable(errorString);
69 }
70
71 void InspectorConsoleAgent::enable(ErrorString&)
72 {
73     if (m_enabled)
74         return;
75
76     m_enabled = true;
77
78     if (m_expiredConsoleMessageCount) {
79         ConsoleMessage expiredMessage(MessageSource::Other, MessageType::Log, MessageLevel::Warning, String::format("%d console messages are not shown.", m_expiredConsoleMessageCount));
80         expiredMessage.addToFrontend(*m_frontendDispatcher, m_injectedScriptManager, false);
81     }
82
83     size_t messageCount = m_consoleMessages.size();
84     for (size_t i = 0; i < messageCount; ++i)
85         m_consoleMessages[i]->addToFrontend(*m_frontendDispatcher, m_injectedScriptManager, false);
86 }
87
88 void InspectorConsoleAgent::disable(ErrorString&)
89 {
90     if (!m_enabled)
91         return;
92
93     m_enabled = false;
94 }
95
96 void InspectorConsoleAgent::clearMessages(ErrorString&)
97 {
98     m_consoleMessages.clear();
99     m_expiredConsoleMessageCount = 0;
100     m_previousMessage = nullptr;
101
102     m_injectedScriptManager.releaseObjectGroup(ASCIILiteral("console"));
103
104     if (m_frontendDispatcher && m_enabled)
105         m_frontendDispatcher->messagesCleared();
106 }
107
108 void InspectorConsoleAgent::reset()
109 {
110     ErrorString unused;
111     clearMessages(unused);
112
113     m_times.clear();
114     m_counts.clear();
115 }
116
117 void InspectorConsoleAgent::addMessageToConsole(std::unique_ptr<ConsoleMessage> message)
118 {
119     if (!m_injectedScriptManager.inspectorEnvironment().developerExtrasEnabled())
120         return;
121
122     if (message->type() == MessageType::Clear) {
123         ErrorString unused;
124         clearMessages(unused);
125     }
126
127     addConsoleMessage(WTF::move(message));
128 }
129
130 Vector<unsigned> InspectorConsoleAgent::consoleMessageArgumentCounts() const
131 {
132     Vector<unsigned> result(m_consoleMessages.size());
133     for (size_t i = 0; i < m_consoleMessages.size(); i++)
134         result[i] = m_consoleMessages[i]->argumentCount();
135     return result;
136 }
137
138 void InspectorConsoleAgent::startTiming(const String& title)
139 {
140     // Follow Firebug's behavior of requiring a title that is not null or
141     // undefined for timing functions
142     if (title.isNull())
143         return;
144
145     m_times.add(title, monotonicallyIncreasingTime());
146 }
147
148 void InspectorConsoleAgent::stopTiming(const String& title, PassRefPtr<ScriptCallStack> callStack)
149 {
150     // Follow Firebug's behavior of requiring a title that is not null or
151     // undefined for timing functions
152     if (title.isNull())
153         return;
154
155     HashMap<String, double>::iterator it = m_times.find(title);
156     if (it == m_times.end())
157         return;
158
159     double startTime = it->value;
160     m_times.remove(it);
161
162     double elapsed = monotonicallyIncreasingTime() - startTime;
163     String message = title + String::format(": %.3fms", elapsed * 1000);
164     addMessageToConsole(std::make_unique<ConsoleMessage>(MessageSource::ConsoleAPI, MessageType::Timing, MessageLevel::Debug, message, callStack));
165 }
166
167 void InspectorConsoleAgent::count(JSC::ExecState* state, PassRefPtr<ScriptArguments> arguments)
168 {
169     RefPtr<ScriptCallStack> callStack(createScriptCallStackForConsole(state, ScriptCallStack::maxCallStackSizeToCapture));
170     const ScriptCallFrame& lastCaller = callStack->at(0);
171     // Follow Firebug's behavior of counting with null and undefined title in
172     // the same bucket as no argument
173     String title;
174     arguments->getFirstArgumentAsString(title);
175     String identifier = title + '@' + lastCaller.sourceURL() + ':' + String::number(lastCaller.lineNumber());
176
177     HashMap<String, unsigned>::iterator it = m_counts.find(identifier);
178     int count;
179     if (it == m_counts.end())
180         count = 1;
181     else {
182         count = it->value + 1;
183         m_counts.remove(it);
184     }
185
186     m_counts.add(identifier, count);
187
188     String message = title + ": " + String::number(count);
189     addMessageToConsole(std::make_unique<ConsoleMessage>(MessageSource::ConsoleAPI, MessageType::Log, MessageLevel::Debug, message, callStack));
190 }
191
192 static bool isGroupMessage(MessageType type)
193 {
194     return type == MessageType::StartGroup
195         || type == MessageType::StartGroupCollapsed
196         || type == MessageType::EndGroup;
197 }
198
199 void InspectorConsoleAgent::addConsoleMessage(std::unique_ptr<ConsoleMessage> consoleMessage)
200 {
201     ASSERT(m_injectedScriptManager.inspectorEnvironment().developerExtrasEnabled());
202     ASSERT_ARG(consoleMessage, consoleMessage);
203
204     if (m_previousMessage && !isGroupMessage(m_previousMessage->type()) && m_previousMessage->isEqual(consoleMessage.get())) {
205         m_previousMessage->incrementCount();
206         if (m_frontendDispatcher && m_enabled)
207             m_previousMessage->updateRepeatCountInConsole(*m_frontendDispatcher);
208     } else {
209         m_previousMessage = consoleMessage.get();
210         m_consoleMessages.append(WTF::move(consoleMessage));
211         if (m_frontendDispatcher && m_enabled)
212             m_previousMessage->addToFrontend(*m_frontendDispatcher, m_injectedScriptManager, true);
213     }
214
215     if (!m_frontendDispatcher && m_consoleMessages.size() >= maximumConsoleMessages) {
216         m_expiredConsoleMessageCount += expireConsoleMessagesStep;
217         m_consoleMessages.remove(0, expireConsoleMessagesStep);
218     }
219 }
220
221 } // namespace Inspector