Web Inspector: JSContext inspection should report exceptions in the console
[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 #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
42 namespace Inspector {
43
44 static const unsigned maximumConsoleMessages = 1000;
45 static const int expireConsoleMessagesStep = 100;
46
47 InspectorConsoleAgent::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
56 InspectorConsoleAgent::~InspectorConsoleAgent()
57 {
58 }
59
60 void 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
66 void InspectorConsoleAgent::willDestroyFrontendAndBackend(InspectorDisconnectReason)
67 {
68     m_frontendDispatcher = nullptr;
69     m_backendDispatcher.clear();
70
71     String errorString;
72     disable(&errorString);
73 }
74
75 void 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
92 void InspectorConsoleAgent::disable(ErrorString*)
93 {
94     if (!m_enabled)
95         return;
96
97     m_enabled = false;
98 }
99
100 void 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
112 void InspectorConsoleAgent::reset()
113 {
114     ErrorString error;
115     clearMessages(&error);
116
117     m_times.clear();
118     m_counts.clear();
119 }
120
121 void 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
134 void 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
147 void 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
160 Vector<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
168 void 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
178 void 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
197 void 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
222 static bool isGroupMessage(MessageType type)
223 {
224     return type == MessageType::StartGroup
225         || type == MessageType::StartGroupCollapsed
226         || type == MessageType::EndGroup;
227 }
228
229 void 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(std::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)