Unreviewed, rolling out r234489.
[WebKit-https.git] / Source / JavaScriptCore / inspector / agents / InspectorConsoleAgent.cpp
1 /*
2  * Copyright (C) 2014, 2015 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 "InspectorHeapAgent.h"
33 #include "ScriptArguments.h"
34 #include "ScriptCallFrame.h"
35 #include "ScriptCallStack.h"
36 #include "ScriptCallStackFactory.h"
37 #include "ScriptObject.h"
38 #include <wtf/text/WTFString.h>
39
40 namespace Inspector {
41
42 static const unsigned maximumConsoleMessages = 100;
43 static const int expireConsoleMessagesStep = 10;
44
45 InspectorConsoleAgent::InspectorConsoleAgent(AgentContext& context, InspectorHeapAgent* heapAgent)
46     : InspectorAgentBase("Console"_s)
47     , m_injectedScriptManager(context.injectedScriptManager)
48     , m_frontendDispatcher(std::make_unique<ConsoleFrontendDispatcher>(context.frontendRouter))
49     , m_backendDispatcher(ConsoleBackendDispatcher::create(context.backendDispatcher, this))
50     , m_heapAgent(heapAgent)
51 {
52 }
53
54 InspectorConsoleAgent::~InspectorConsoleAgent()
55 {
56 }
57
58 void InspectorConsoleAgent::didCreateFrontendAndBackend(FrontendRouter*, BackendDispatcher*)
59 {
60 }
61
62 void InspectorConsoleAgent::willDestroyFrontendAndBackend(DisconnectReason)
63 {
64     String errorString;
65     disable(errorString);
66 }
67
68 void InspectorConsoleAgent::discardValues()
69 {
70     m_consoleMessages.clear();
71     m_expiredConsoleMessageCount = 0;
72 }
73
74 void InspectorConsoleAgent::enable(ErrorString&)
75 {
76     if (m_enabled)
77         return;
78
79     m_enabled = true;
80
81     if (m_expiredConsoleMessageCount) {
82         ConsoleMessage expiredMessage(MessageSource::Other, MessageType::Log, MessageLevel::Warning, String::format("%d console messages are not shown.", m_expiredConsoleMessageCount));
83         expiredMessage.addToFrontend(*m_frontendDispatcher, m_injectedScriptManager, false);
84     }
85
86     Vector<std::unique_ptr<ConsoleMessage>> messages;
87     m_consoleMessages.swap(messages);
88
89     for (size_t i = 0; i < messages.size(); ++i)
90         messages[i]->addToFrontend(*m_frontendDispatcher, m_injectedScriptManager, false);
91 }
92
93 void InspectorConsoleAgent::disable(ErrorString&)
94 {
95     if (!m_enabled)
96         return;
97
98     m_enabled = false;
99 }
100
101 void InspectorConsoleAgent::clearMessages(ErrorString&)
102 {
103     m_consoleMessages.clear();
104     m_expiredConsoleMessageCount = 0;
105
106     m_injectedScriptManager.releaseObjectGroup("console"_s);
107
108     if (m_enabled)
109         m_frontendDispatcher->messagesCleared();
110 }
111
112 void InspectorConsoleAgent::reset()
113 {
114     ErrorString unused;
115     clearMessages(unused);
116
117     m_times.clear();
118     m_counts.clear();
119 }
120
121 void InspectorConsoleAgent::addMessageToConsole(std::unique_ptr<ConsoleMessage> message)
122 {
123     if (!m_injectedScriptManager.inspectorEnvironment().developerExtrasEnabled())
124         return;
125
126     if (message->type() == MessageType::Clear) {
127         ErrorString unused;
128         clearMessages(unused);
129     }
130
131     addConsoleMessage(WTFMove(message));
132 }
133
134 void InspectorConsoleAgent::startTiming(const String& title)
135 {
136     ASSERT(!title.isNull());
137     if (title.isNull())
138         return;
139
140     auto result = m_times.add(title, MonotonicTime::now());
141
142     if (!result.isNewEntry) {
143         // FIXME: Send an enum to the frontend for localization?
144         String warning = makeString("Timer \"", title, "\" already exists");
145         addMessageToConsole(std::make_unique<ConsoleMessage>(MessageSource::ConsoleAPI, MessageType::Timing, MessageLevel::Warning, warning));
146     }
147 }
148
149 void InspectorConsoleAgent::stopTiming(const String& title, Ref<ScriptCallStack>&& callStack)
150 {
151     ASSERT(!title.isNull());
152     if (title.isNull())
153         return;
154
155     auto it = m_times.find(title);
156     if (it == m_times.end()) {
157         // FIXME: Send an enum to the frontend for localization?
158         String warning = makeString("Timer \"", title, "\" does not exist");
159         addMessageToConsole(std::make_unique<ConsoleMessage>(MessageSource::ConsoleAPI, MessageType::Timing, MessageLevel::Warning, warning));
160         return;
161     }
162
163     MonotonicTime startTime = it->value;
164     m_times.remove(it);
165
166     Seconds elapsed = MonotonicTime::now() - startTime;
167     String message = title + String::format(": %.3fms", elapsed.milliseconds());
168     addMessageToConsole(std::make_unique<ConsoleMessage>(MessageSource::ConsoleAPI, MessageType::Timing, MessageLevel::Debug, message, WTFMove(callStack)));
169 }
170
171 void InspectorConsoleAgent::takeHeapSnapshot(const String& title)
172 {
173     if (!m_injectedScriptManager.inspectorEnvironment().developerExtrasEnabled())
174         return;
175
176     ErrorString ignored;
177     double timestamp;
178     String snapshotData;
179     m_heapAgent->snapshot(ignored, &timestamp, &snapshotData);
180
181     m_frontendDispatcher->heapSnapshot(timestamp, snapshotData, title.isEmpty() ? nullptr : &title);
182 }
183
184 void InspectorConsoleAgent::count(JSC::ExecState* state, Ref<ScriptArguments>&& arguments)
185 {
186     Ref<ScriptCallStack> callStack = createScriptCallStackForConsole(state);
187
188     String title;
189     String identifier;
190     if (!arguments->argumentCount()) {
191         // '@' prefix for engine generated labels.
192         title = "Global"_s;
193         identifier = makeString('@', title);
194     } else {
195         // '#' prefix for user labels.
196         arguments->getFirstArgumentAsString(title);
197         identifier = makeString('#', title);
198     }
199
200     auto result = m_counts.add(identifier, 1);
201     if (!result.isNewEntry)
202         result.iterator->value += 1;
203
204     // FIXME: Web Inspector should have a better UI for counters, but for now we just log an updated counter value.
205
206     String message = makeString(title, ": ", String::number(result.iterator->value));
207     addMessageToConsole(std::make_unique<ConsoleMessage>(MessageSource::ConsoleAPI, MessageType::Log, MessageLevel::Debug, message, WTFMove(callStack)));
208 }
209
210 static bool isGroupMessage(MessageType type)
211 {
212     return type == MessageType::StartGroup
213         || type == MessageType::StartGroupCollapsed
214         || type == MessageType::EndGroup;
215 }
216
217 void InspectorConsoleAgent::addConsoleMessage(std::unique_ptr<ConsoleMessage> consoleMessage)
218 {
219     ASSERT(m_injectedScriptManager.inspectorEnvironment().developerExtrasEnabled());
220     ASSERT_ARG(consoleMessage, consoleMessage);
221
222     ConsoleMessage* previousMessage = m_consoleMessages.isEmpty() ? nullptr : m_consoleMessages.last().get();
223
224     if (previousMessage && !isGroupMessage(previousMessage->type()) && previousMessage->isEqual(consoleMessage.get())) {
225         previousMessage->incrementCount();
226         if (m_enabled)
227             previousMessage->updateRepeatCountInConsole(*m_frontendDispatcher);
228     } else {
229         ConsoleMessage* newMessage = consoleMessage.get();
230         m_consoleMessages.append(WTFMove(consoleMessage));
231         if (m_enabled)
232             newMessage->addToFrontend(*m_frontendDispatcher, m_injectedScriptManager, true);
233
234         if (m_consoleMessages.size() >= maximumConsoleMessages) {
235             m_expiredConsoleMessageCount += expireConsoleMessagesStep;
236             m_consoleMessages.remove(0, expireConsoleMessagesStep);
237         }
238     }
239 }
240
241 void InspectorConsoleAgent::getLoggingChannels(ErrorString&, RefPtr<JSON::ArrayOf<Protocol::Console::Channel>>& channels)
242 {
243     // Default implementation has no logging channels.
244     channels = JSON::ArrayOf<Protocol::Console::Channel>::create();
245 }
246
247 void InspectorConsoleAgent::setLoggingChannelLevel(ErrorString& errorString, const String&, const String&)
248 {
249     errorString = "No such channel to enable"_s;
250 }
251
252 } // namespace Inspector