Replace WTF::move with WTFMove
[WebKit-https.git] / Source / JavaScriptCore / inspector / agents / InspectorDebuggerAgent.cpp
1 /*
2  * Copyright (C) 2010, 2013, 2015 Apple Inc. All rights reserved.
3  * Copyright (C) 2010, 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  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer.
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution.
14  * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
15  *     its contributors may be used to endorse or promote products derived
16  *     from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include "config.h"
31 #include "InspectorDebuggerAgent.h"
32
33 #include "ContentSearchUtilities.h"
34 #include "InjectedScript.h"
35 #include "InjectedScriptManager.h"
36 #include "InspectorFrontendRouter.h"
37 #include "InspectorValues.h"
38 #include "RegularExpression.h"
39 #include "ScriptDebugServer.h"
40 #include "ScriptObject.h"
41 #include "ScriptValue.h"
42 #include <wtf/Stopwatch.h>
43 #include <wtf/text/WTFString.h>
44
45 namespace Inspector {
46
47 const char* InspectorDebuggerAgent::backtraceObjectGroup = "backtrace";
48
49 // Objects created and retained by evaluating breakpoint actions are put into object groups
50 // according to the breakpoint action identifier assigned by the frontend. A breakpoint may
51 // have several object groups, and objects from several backend breakpoint action instances may
52 // create objects in the same group.
53 static String objectGroupForBreakpointAction(const ScriptBreakpointAction& action)
54 {
55     DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, objectGroup, ("breakpoint-action-", AtomicString::ConstructFromLiteral));
56     return makeString(objectGroup, String::number(action.identifier));
57 }
58
59 InspectorDebuggerAgent::InspectorDebuggerAgent(AgentContext& context)
60     : InspectorAgentBase(ASCIILiteral("Debugger"))
61     , m_injectedScriptManager(context.injectedScriptManager)
62     , m_frontendDispatcher(std::make_unique<DebuggerFrontendDispatcher>(context.frontendRouter))
63     , m_backendDispatcher(DebuggerBackendDispatcher::create(context.backendDispatcher, this))
64     , m_scriptDebugServer(context.environment.scriptDebugServer())
65     , m_continueToLocationBreakpointID(JSC::noBreakpointID)
66 {
67     // FIXME: make breakReason optional so that there was no need to init it with "other".
68     clearBreakDetails();
69 }
70
71 InspectorDebuggerAgent::~InspectorDebuggerAgent()
72 {
73 }
74
75 void InspectorDebuggerAgent::didCreateFrontendAndBackend(FrontendRouter*, BackendDispatcher*)
76 {
77 }
78
79 void InspectorDebuggerAgent::willDestroyFrontendAndBackend(DisconnectReason reason)
80 {
81     bool skipRecompile = reason == DisconnectReason::InspectedTargetDestroyed;
82     disable(skipRecompile);
83 }
84
85 void InspectorDebuggerAgent::enable()
86 {
87     if (m_enabled)
88         return;
89
90     m_scriptDebugServer.setBreakpointsActivated(true);
91     m_scriptDebugServer.addListener(this);
92
93     if (m_listener)
94         m_listener->debuggerWasEnabled();
95
96     m_enabled = true;
97 }
98
99 void InspectorDebuggerAgent::disable(bool isBeingDestroyed)
100 {
101     if (!m_enabled)
102         return;
103
104     m_scriptDebugServer.removeListener(this, isBeingDestroyed);
105     clearInspectorBreakpointState();
106
107     ASSERT(m_javaScriptBreakpoints.isEmpty());
108
109     if (m_listener)
110         m_listener->debuggerWasDisabled();
111
112     m_enabled = false;
113 }
114
115 void InspectorDebuggerAgent::enable(ErrorString&)
116 {
117     enable();
118 }
119
120 void InspectorDebuggerAgent::disable(ErrorString&)
121 {
122     disable(false);
123 }
124
125 void InspectorDebuggerAgent::setBreakpointsActive(ErrorString&, bool active)
126 {
127     if (active)
128         m_scriptDebugServer.activateBreakpoints();
129     else
130         m_scriptDebugServer.deactivateBreakpoints();
131 }
132
133 bool InspectorDebuggerAgent::isPaused()
134 {
135     return m_scriptDebugServer.isPaused();
136 }
137
138 void InspectorDebuggerAgent::setSuppressAllPauses(bool suppress)
139 {
140     m_scriptDebugServer.setSuppressAllPauses(suppress);
141 }
142
143 static RefPtr<InspectorObject> buildAssertPauseReason(const String& message)
144 {
145     auto reason = Inspector::Protocol::Debugger::AssertPauseReason::create().release();
146     if (!message.isNull())
147         reason->setMessage(message);
148     return reason->openAccessors();
149 }
150
151 static RefPtr<InspectorObject> buildCSPViolationPauseReason(const String& directiveText)
152 {
153     auto reason = Inspector::Protocol::Debugger::CSPViolationPauseReason::create()
154         .setDirective(directiveText)
155         .release();
156     return reason->openAccessors();
157 }
158
159 RefPtr<InspectorObject> InspectorDebuggerAgent::buildBreakpointPauseReason(JSC::BreakpointID debuggerBreakpointIdentifier)
160 {
161     ASSERT(debuggerBreakpointIdentifier != JSC::noBreakpointID);
162     auto it = m_debuggerBreakpointIdentifierToInspectorBreakpointIdentifier.find(debuggerBreakpointIdentifier);
163     if (it == m_debuggerBreakpointIdentifierToInspectorBreakpointIdentifier.end())
164         return nullptr;
165
166     auto reason = Inspector::Protocol::Debugger::BreakpointPauseReason::create()
167         .setBreakpointId(it->value)
168         .release();
169     return reason->openAccessors();
170 }
171
172 RefPtr<InspectorObject> InspectorDebuggerAgent::buildExceptionPauseReason(const Deprecated::ScriptValue& exception, const InjectedScript& injectedScript)
173 {
174     ASSERT(!exception.hasNoValue());
175     if (exception.hasNoValue())
176         return nullptr;
177
178     ASSERT(!injectedScript.hasNoValue());
179     if (injectedScript.hasNoValue())
180         return nullptr;
181
182     return injectedScript.wrapObject(exception, InspectorDebuggerAgent::backtraceObjectGroup)->openAccessors();
183 }
184
185 void InspectorDebuggerAgent::handleConsoleAssert(const String& message)
186 {
187     if (m_scriptDebugServer.pauseOnExceptionsState() != JSC::Debugger::DontPauseOnExceptions)
188         breakProgram(DebuggerFrontendDispatcher::Reason::Assert, buildAssertPauseReason(message));
189 }
190
191 static Ref<InspectorObject> buildObjectForBreakpointCookie(const String& url, int lineNumber, int columnNumber, const String& condition, RefPtr<InspectorArray>& actions, bool isRegex, bool autoContinue, unsigned ignoreCount)
192 {
193     Ref<InspectorObject> breakpointObject = InspectorObject::create();
194     breakpointObject->setString(ASCIILiteral("url"), url);
195     breakpointObject->setInteger(ASCIILiteral("lineNumber"), lineNumber);
196     breakpointObject->setInteger(ASCIILiteral("columnNumber"), columnNumber);
197     breakpointObject->setString(ASCIILiteral("condition"), condition);
198     breakpointObject->setBoolean(ASCIILiteral("isRegex"), isRegex);
199     breakpointObject->setBoolean(ASCIILiteral("autoContinue"), autoContinue);
200     breakpointObject->setInteger(ASCIILiteral("ignoreCount"), ignoreCount);
201
202     if (actions)
203         breakpointObject->setArray(ASCIILiteral("actions"), actions);
204
205     return breakpointObject;
206 }
207
208 static bool matches(const String& url, const String& pattern, bool isRegex)
209 {
210     if (isRegex) {
211         JSC::Yarr::RegularExpression regex(pattern, TextCaseSensitive);
212         return regex.match(url) != -1;
213     }
214     return url == pattern;
215 }
216
217 static bool breakpointActionTypeForString(const String& typeString, ScriptBreakpointActionType* output)
218 {
219     if (typeString == Inspector::Protocol::getEnumConstantValue(Inspector::Protocol::Debugger::BreakpointAction::Type::Log)) {
220         *output = ScriptBreakpointActionTypeLog;
221         return true;
222     }
223     if (typeString == Inspector::Protocol::getEnumConstantValue(Inspector::Protocol::Debugger::BreakpointAction::Type::Evaluate)) {
224         *output = ScriptBreakpointActionTypeEvaluate;
225         return true;
226     }
227     if (typeString == Inspector::Protocol::getEnumConstantValue(Inspector::Protocol::Debugger::BreakpointAction::Type::Sound)) {
228         *output = ScriptBreakpointActionTypeSound;
229         return true;
230     }
231     if (typeString == Inspector::Protocol::getEnumConstantValue(Inspector::Protocol::Debugger::BreakpointAction::Type::Probe)) {
232         *output = ScriptBreakpointActionTypeProbe;
233         return true;
234     }
235
236     return false;
237 }
238
239 bool InspectorDebuggerAgent::breakpointActionsFromProtocol(ErrorString& errorString, RefPtr<InspectorArray>& actions, BreakpointActions* result)
240 {
241     if (!actions)
242         return true;
243
244     unsigned actionsLength = actions->length();
245     if (!actionsLength)
246         return true;
247
248     result->reserveCapacity(actionsLength);
249     for (unsigned i = 0; i < actionsLength; ++i) {
250         RefPtr<InspectorValue> value = actions->get(i);
251         RefPtr<InspectorObject> object;
252         if (!value->asObject(object)) {
253             errorString = ASCIILiteral("BreakpointAction of incorrect type, expected object");
254             return false;
255         }
256
257         String typeString;
258         if (!object->getString(ASCIILiteral("type"), typeString)) {
259             errorString = ASCIILiteral("BreakpointAction had type missing");
260             return false;
261         }
262
263         ScriptBreakpointActionType type;
264         if (!breakpointActionTypeForString(typeString, &type)) {
265             errorString = ASCIILiteral("BreakpointAction had unknown type");
266             return false;
267         }
268
269         // Specifying an identifier is optional. They are used to correlate probe samples
270         // in the frontend across multiple backend probe actions and segregate object groups.
271         int identifier = 0;
272         object->getInteger(ASCIILiteral("id"), identifier);
273
274         String data;
275         object->getString(ASCIILiteral("data"), data);
276
277         result->append(ScriptBreakpointAction(type, identifier, data));
278     }
279
280     return true;
281 }
282
283 void InspectorDebuggerAgent::setBreakpointByUrl(ErrorString& errorString, int lineNumber, const String* const optionalURL, const String* const optionalURLRegex, const int* const optionalColumnNumber, const InspectorObject* options, Inspector::Protocol::Debugger::BreakpointId* outBreakpointIdentifier, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Debugger::Location>>& locations)
284 {
285     locations = Inspector::Protocol::Array<Inspector::Protocol::Debugger::Location>::create();
286     if (!optionalURL == !optionalURLRegex) {
287         errorString = ASCIILiteral("Either url or urlRegex must be specified.");
288         return;
289     }
290
291     String url = optionalURL ? *optionalURL : *optionalURLRegex;
292     int columnNumber = optionalColumnNumber ? *optionalColumnNumber : 0;
293     bool isRegex = optionalURLRegex;
294
295     String breakpointIdentifier = (isRegex ? "/" + url + "/" : url) + ':' + String::number(lineNumber) + ':' + String::number(columnNumber);
296     if (m_javaScriptBreakpoints.contains(breakpointIdentifier)) {
297         errorString = ASCIILiteral("Breakpoint at specified location already exists.");
298         return;
299     }
300
301     String condition = emptyString();
302     bool autoContinue = false;
303     unsigned ignoreCount = 0;
304     RefPtr<InspectorArray> actions;
305     if (options) {
306         options->getString(ASCIILiteral("condition"), condition);
307         options->getBoolean(ASCIILiteral("autoContinue"), autoContinue);
308         options->getArray(ASCIILiteral("actions"), actions);
309         options->getInteger(ASCIILiteral("ignoreCount"), ignoreCount);
310     }
311
312     BreakpointActions breakpointActions;
313     if (!breakpointActionsFromProtocol(errorString, actions, &breakpointActions))
314         return;
315
316     m_javaScriptBreakpoints.set(breakpointIdentifier, buildObjectForBreakpointCookie(url, lineNumber, columnNumber, condition, actions, isRegex, autoContinue, ignoreCount));
317
318     ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition, breakpointActions, autoContinue, ignoreCount);
319     for (ScriptsMap::iterator it = m_scripts.begin(); it != m_scripts.end(); ++it) {
320         String scriptURL = !it->value.sourceURL.isEmpty() ? it->value.sourceURL : it->value.url;
321         if (!matches(scriptURL, url, isRegex))
322             continue;
323
324         RefPtr<Inspector::Protocol::Debugger::Location> location = resolveBreakpoint(breakpointIdentifier, it->key, breakpoint);
325         if (location)
326             locations->addItem(WTFMove(location));
327     }
328     *outBreakpointIdentifier = breakpointIdentifier;
329 }
330
331 static bool parseLocation(ErrorString& errorString, const InspectorObject& location, JSC::SourceID& sourceID, unsigned& lineNumber, unsigned& columnNumber)
332 {
333     String scriptIDStr;
334     if (!location.getString(ASCIILiteral("scriptId"), scriptIDStr) || !location.getInteger(ASCIILiteral("lineNumber"), lineNumber)) {
335         sourceID = JSC::noSourceID;
336         errorString = ASCIILiteral("scriptId and lineNumber are required.");
337         return false;
338     }
339
340     sourceID = scriptIDStr.toIntPtr();
341     columnNumber = 0;
342     location.getInteger(ASCIILiteral("columnNumber"), columnNumber);
343     return true;
344 }
345
346 void InspectorDebuggerAgent::setBreakpoint(ErrorString& errorString, const InspectorObject& location, const InspectorObject* options, Inspector::Protocol::Debugger::BreakpointId* outBreakpointIdentifier, RefPtr<Inspector::Protocol::Debugger::Location>& actualLocation)
347 {
348     JSC::SourceID sourceID;
349     unsigned lineNumber;
350     unsigned columnNumber;
351     if (!parseLocation(errorString, location, sourceID, lineNumber, columnNumber))
352         return;
353
354     String condition = emptyString();
355     bool autoContinue = false;
356     unsigned ignoreCount = 0;
357     RefPtr<InspectorArray> actions;
358     if (options) {
359         options->getString(ASCIILiteral("condition"), condition);
360         options->getBoolean(ASCIILiteral("autoContinue"), autoContinue);
361         options->getArray(ASCIILiteral("actions"), actions);
362         options->getInteger(ASCIILiteral("ignoreCount"), ignoreCount);
363     }
364
365     BreakpointActions breakpointActions;
366     if (!breakpointActionsFromProtocol(errorString, actions, &breakpointActions))
367         return;
368
369     String breakpointIdentifier = String::number(sourceID) + ':' + String::number(lineNumber) + ':' + String::number(columnNumber);
370     if (m_breakpointIdentifierToDebugServerBreakpointIDs.find(breakpointIdentifier) != m_breakpointIdentifierToDebugServerBreakpointIDs.end()) {
371         errorString = ASCIILiteral("Breakpoint at specified location already exists.");
372         return;
373     }
374
375     ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition, breakpointActions, autoContinue, ignoreCount);
376     actualLocation = resolveBreakpoint(breakpointIdentifier, sourceID, breakpoint);
377     if (!actualLocation) {
378         errorString = ASCIILiteral("Could not resolve breakpoint");
379         return;
380     }
381
382     *outBreakpointIdentifier = breakpointIdentifier;
383 }
384
385 void InspectorDebuggerAgent::removeBreakpoint(ErrorString&, const String& breakpointIdentifier)
386 {
387     m_javaScriptBreakpoints.remove(breakpointIdentifier);
388
389     for (JSC::BreakpointID breakpointID : m_breakpointIdentifierToDebugServerBreakpointIDs.take(breakpointIdentifier)) {
390         m_debuggerBreakpointIdentifierToInspectorBreakpointIdentifier.remove(breakpointID);
391
392         const BreakpointActions& breakpointActions = m_scriptDebugServer.getActionsForBreakpoint(breakpointID);
393         for (auto& action : breakpointActions)
394             m_injectedScriptManager.releaseObjectGroup(objectGroupForBreakpointAction(action));
395
396         m_scriptDebugServer.removeBreakpoint(breakpointID);
397     }
398 }
399
400 void InspectorDebuggerAgent::continueToLocation(ErrorString& errorString, const InspectorObject& location)
401 {
402     if (m_continueToLocationBreakpointID != JSC::noBreakpointID) {
403         m_scriptDebugServer.removeBreakpoint(m_continueToLocationBreakpointID);
404         m_continueToLocationBreakpointID = JSC::noBreakpointID;
405     }
406
407     JSC::SourceID sourceID;
408     unsigned lineNumber;
409     unsigned columnNumber;
410     if (!parseLocation(errorString, location, sourceID, lineNumber, columnNumber))
411         return;
412
413     ScriptBreakpoint breakpoint(lineNumber, columnNumber, "", false, 0);
414     m_continueToLocationBreakpointID = m_scriptDebugServer.setBreakpoint(sourceID, breakpoint, &lineNumber, &columnNumber);
415     resume(errorString);
416 }
417
418 RefPtr<Inspector::Protocol::Debugger::Location> InspectorDebuggerAgent::resolveBreakpoint(const String& breakpointIdentifier, JSC::SourceID sourceID, const ScriptBreakpoint& breakpoint)
419 {
420     ScriptsMap::iterator scriptIterator = m_scripts.find(sourceID);
421     if (scriptIterator == m_scripts.end())
422         return nullptr;
423     Script& script = scriptIterator->value;
424     if (breakpoint.lineNumber < script.startLine || script.endLine < breakpoint.lineNumber)
425         return nullptr;
426
427     unsigned actualLineNumber;
428     unsigned actualColumnNumber;
429     JSC::BreakpointID debugServerBreakpointID = m_scriptDebugServer.setBreakpoint(sourceID, breakpoint, &actualLineNumber, &actualColumnNumber);
430     if (debugServerBreakpointID == JSC::noBreakpointID)
431         return nullptr;
432
433     BreakpointIdentifierToDebugServerBreakpointIDsMap::iterator debugServerBreakpointIDsIterator = m_breakpointIdentifierToDebugServerBreakpointIDs.find(breakpointIdentifier);
434     if (debugServerBreakpointIDsIterator == m_breakpointIdentifierToDebugServerBreakpointIDs.end())
435         debugServerBreakpointIDsIterator = m_breakpointIdentifierToDebugServerBreakpointIDs.set(breakpointIdentifier, Vector<JSC::BreakpointID>()).iterator;
436     debugServerBreakpointIDsIterator->value.append(debugServerBreakpointID);
437     
438     m_debuggerBreakpointIdentifierToInspectorBreakpointIdentifier.set(debugServerBreakpointID, breakpointIdentifier);
439
440     auto location = Inspector::Protocol::Debugger::Location::create()
441         .setScriptId(String::number(sourceID))
442         .setLineNumber(actualLineNumber)
443         .release();
444     location->setColumnNumber(actualColumnNumber);
445     return WTFMove(location);
446 }
447
448 void InspectorDebuggerAgent::searchInContent(ErrorString& error, const String& scriptIDStr, const String& query, const bool* optionalCaseSensitive, const bool* optionalIsRegex, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::GenericTypes::SearchMatch>>& results)
449 {
450     JSC::SourceID sourceID = scriptIDStr.toIntPtr();
451     auto it = m_scripts.find(sourceID);
452     if (it == m_scripts.end()) {
453         error = ASCIILiteral("No script for id: ") + scriptIDStr;
454         return;
455     }
456
457     bool isRegex = optionalIsRegex ? *optionalIsRegex : false;
458     bool caseSensitive = optionalCaseSensitive ? *optionalCaseSensitive : false;
459     results = ContentSearchUtilities::searchInTextByLines(it->value.source, query, caseSensitive, isRegex);
460 }
461
462 void InspectorDebuggerAgent::getScriptSource(ErrorString& error, const String& scriptIDStr, String* scriptSource)
463 {
464     JSC::SourceID sourceID = scriptIDStr.toIntPtr();
465     ScriptsMap::iterator it = m_scripts.find(sourceID);
466     if (it != m_scripts.end())
467         *scriptSource = it->value.source;
468     else
469         error = ASCIILiteral("No script for id: ") + scriptIDStr;
470 }
471
472 void InspectorDebuggerAgent::getFunctionDetails(ErrorString& errorString, const String& functionId, RefPtr<Inspector::Protocol::Debugger::FunctionDetails>& details)
473 {
474     InjectedScript injectedScript = m_injectedScriptManager.injectedScriptForObjectId(functionId);
475     if (injectedScript.hasNoValue()) {
476         errorString = ASCIILiteral("Function object id is obsolete");
477         return;
478     }
479
480     injectedScript.getFunctionDetails(errorString, functionId, &details);
481 }
482
483 void InspectorDebuggerAgent::schedulePauseOnNextStatement(DebuggerFrontendDispatcher::Reason breakReason, RefPtr<InspectorObject>&& data)
484 {
485     if (m_javaScriptPauseScheduled)
486         return;
487
488     m_breakReason = breakReason;
489     m_breakAuxData = WTFMove(data);
490     m_scriptDebugServer.setPauseOnNextStatement(true);
491 }
492
493 void InspectorDebuggerAgent::cancelPauseOnNextStatement()
494 {
495     if (m_javaScriptPauseScheduled)
496         return;
497
498     clearBreakDetails();
499     m_scriptDebugServer.setPauseOnNextStatement(false);
500 }
501
502 void InspectorDebuggerAgent::pause(ErrorString&)
503 {
504     schedulePauseOnNextStatement(DebuggerFrontendDispatcher::Reason::PauseOnNextStatement, nullptr);
505
506     m_javaScriptPauseScheduled = true;
507 }
508
509 void InspectorDebuggerAgent::resume(ErrorString& errorString)
510 {
511     if (!assertPaused(errorString))
512         return;
513
514     m_scriptDebugServer.continueProgram();
515 }
516
517 void InspectorDebuggerAgent::stepOver(ErrorString& errorString)
518 {
519     if (!assertPaused(errorString))
520         return;
521
522     m_scriptDebugServer.stepOverStatement();
523 }
524
525 void InspectorDebuggerAgent::stepInto(ErrorString& errorString)
526 {
527     if (!assertPaused(errorString))
528         return;
529
530     m_scriptDebugServer.stepIntoStatement();
531
532     if (m_listener)
533         m_listener->stepInto();
534 }
535
536 void InspectorDebuggerAgent::stepOut(ErrorString& errorString)
537 {
538     if (!assertPaused(errorString))
539         return;
540
541     m_scriptDebugServer.stepOutOfFunction();
542 }
543
544 void InspectorDebuggerAgent::setPauseOnExceptions(ErrorString& errorString, const String& stringPauseState)
545 {
546     JSC::Debugger::PauseOnExceptionsState pauseState;
547     if (stringPauseState == "none")
548         pauseState = JSC::Debugger::DontPauseOnExceptions;
549     else if (stringPauseState == "all")
550         pauseState = JSC::Debugger::PauseOnAllExceptions;
551     else if (stringPauseState == "uncaught")
552         pauseState = JSC::Debugger::PauseOnUncaughtExceptions;
553     else {
554         errorString = ASCIILiteral("Unknown pause on exceptions mode: ") + stringPauseState;
555         return;
556     }
557
558     m_scriptDebugServer.setPauseOnExceptionsState(static_cast<JSC::Debugger::PauseOnExceptionsState>(pauseState));
559     if (m_scriptDebugServer.pauseOnExceptionsState() != pauseState)
560         errorString = ASCIILiteral("Internal error. Could not change pause on exceptions state");
561 }
562
563 void InspectorDebuggerAgent::evaluateOnCallFrame(ErrorString& errorString, const String& callFrameId, const String& expression, const String* const objectGroup, const bool* const includeCommandLineAPI, const bool* const doNotPauseOnExceptionsAndMuteConsole, const bool* const returnByValue, const bool* generatePreview, const bool* saveResult, RefPtr<Inspector::Protocol::Runtime::RemoteObject>& result, Inspector::Protocol::OptOutput<bool>* wasThrown, Inspector::Protocol::OptOutput<int>* savedResultIndex)
564 {
565     InjectedScript injectedScript = m_injectedScriptManager.injectedScriptForObjectId(callFrameId);
566     if (injectedScript.hasNoValue()) {
567         errorString = ASCIILiteral("Inspected frame has gone");
568         return;
569     }
570
571     JSC::Debugger::PauseOnExceptionsState previousPauseOnExceptionsState = m_scriptDebugServer.pauseOnExceptionsState();
572     if (doNotPauseOnExceptionsAndMuteConsole ? *doNotPauseOnExceptionsAndMuteConsole : false) {
573         if (previousPauseOnExceptionsState != JSC::Debugger::DontPauseOnExceptions)
574             m_scriptDebugServer.setPauseOnExceptionsState(JSC::Debugger::DontPauseOnExceptions);
575         muteConsole();
576     }
577
578     injectedScript.evaluateOnCallFrame(errorString, m_currentCallStack, callFrameId, expression, objectGroup ? *objectGroup : "", includeCommandLineAPI ? *includeCommandLineAPI : false, returnByValue ? *returnByValue : false, generatePreview ? *generatePreview : false, saveResult ? *saveResult : false, &result, wasThrown, savedResultIndex);
579
580     if (doNotPauseOnExceptionsAndMuteConsole ? *doNotPauseOnExceptionsAndMuteConsole : false) {
581         unmuteConsole();
582         if (m_scriptDebugServer.pauseOnExceptionsState() != previousPauseOnExceptionsState)
583             m_scriptDebugServer.setPauseOnExceptionsState(previousPauseOnExceptionsState);
584     }
585 }
586
587 void InspectorDebuggerAgent::setOverlayMessage(ErrorString&, const String*)
588 {
589 }
590
591 void InspectorDebuggerAgent::scriptExecutionBlockedByCSP(const String& directiveText)
592 {
593     if (m_scriptDebugServer.pauseOnExceptionsState() != JSC::Debugger::DontPauseOnExceptions)
594         breakProgram(DebuggerFrontendDispatcher::Reason::CSPViolation, buildCSPViolationPauseReason(directiveText));
595 }
596
597 Ref<Inspector::Protocol::Array<Inspector::Protocol::Debugger::CallFrame>> InspectorDebuggerAgent::currentCallFrames(const InjectedScript& injectedScript)
598 {
599     ASSERT(!injectedScript.hasNoValue());
600     if (injectedScript.hasNoValue())
601         return Inspector::Protocol::Array<Inspector::Protocol::Debugger::CallFrame>::create();
602
603     return injectedScript.wrapCallFrames(m_currentCallStack);
604 }
605
606 String InspectorDebuggerAgent::sourceMapURLForScript(const Script& script)
607 {
608     return script.sourceMappingURL;
609 }
610
611 void InspectorDebuggerAgent::didParseSource(JSC::SourceID sourceID, const Script& script)
612 {
613     bool hasSourceURL = !script.sourceURL.isEmpty();
614     String scriptURL = hasSourceURL ? script.sourceURL : script.url;
615     bool* hasSourceURLParam = hasSourceURL ? &hasSourceURL : nullptr;
616     String sourceMappingURL = sourceMapURLForScript(script);
617     String* sourceMapURLParam = sourceMappingURL.isNull() ? nullptr : &sourceMappingURL;
618     const bool* isContentScript = script.isContentScript ? &script.isContentScript : nullptr;
619     String scriptIDStr = String::number(sourceID);
620     m_frontendDispatcher->scriptParsed(scriptIDStr, scriptURL, script.startLine, script.startColumn, script.endLine, script.endColumn, isContentScript, sourceMapURLParam, hasSourceURLParam);
621
622     m_scripts.set(sourceID, script);
623
624     if (scriptURL.isEmpty())
625         return;
626
627     for (auto it = m_javaScriptBreakpoints.begin(), end = m_javaScriptBreakpoints.end(); it != end; ++it) {
628         RefPtr<InspectorObject> breakpointObject;
629         if (!it->value->asObject(breakpointObject))
630             return;
631
632         bool isRegex;
633         breakpointObject->getBoolean(ASCIILiteral("isRegex"), isRegex);
634         String url;
635         breakpointObject->getString(ASCIILiteral("url"), url);
636         if (!matches(scriptURL, url, isRegex))
637             continue;
638
639         ScriptBreakpoint breakpoint;
640         breakpointObject->getInteger(ASCIILiteral("lineNumber"), breakpoint.lineNumber);
641         breakpointObject->getInteger(ASCIILiteral("columnNumber"), breakpoint.columnNumber);
642         breakpointObject->getString(ASCIILiteral("condition"), breakpoint.condition);
643         breakpointObject->getBoolean(ASCIILiteral("autoContinue"), breakpoint.autoContinue);
644         breakpointObject->getInteger(ASCIILiteral("ignoreCount"), breakpoint.ignoreCount);
645         ErrorString errorString;
646         RefPtr<InspectorArray> actions;
647         breakpointObject->getArray(ASCIILiteral("actions"), actions);
648         if (!breakpointActionsFromProtocol(errorString, actions, &breakpoint.actions)) {
649             ASSERT_NOT_REACHED();
650             continue;
651         }
652
653         RefPtr<Inspector::Protocol::Debugger::Location> location = resolveBreakpoint(it->key, sourceID, breakpoint);
654         if (location)
655             m_frontendDispatcher->breakpointResolved(it->key, location);
656     }
657 }
658
659 void InspectorDebuggerAgent::failedToParseSource(const String& url, const String& data, int firstLine, int errorLine, const String& errorMessage)
660 {
661     m_frontendDispatcher->scriptFailedToParse(url, data, firstLine, errorLine, errorMessage);
662 }
663
664 void InspectorDebuggerAgent::didPause(JSC::ExecState* scriptState, const Deprecated::ScriptValue& callFrames, const Deprecated::ScriptValue& exceptionOrCaughtValue)
665 {
666     ASSERT(scriptState && !m_pausedScriptState);
667     m_pausedScriptState = scriptState;
668     m_currentCallStack = callFrames;
669
670     InjectedScript injectedScript = m_injectedScriptManager.injectedScriptFor(scriptState);
671
672     // If a high level pause pause reason is not already set, try to infer a reason from the debugger.
673     if (m_breakReason == DebuggerFrontendDispatcher::Reason::Other) {
674         switch (m_scriptDebugServer.reasonForPause()) {
675         case JSC::Debugger::PausedForBreakpoint: {
676             JSC::BreakpointID debuggerBreakpointId = m_scriptDebugServer.pausingBreakpointID();
677             if (debuggerBreakpointId != m_continueToLocationBreakpointID) {
678                 m_breakReason = DebuggerFrontendDispatcher::Reason::Breakpoint;
679                 m_breakAuxData = buildBreakpointPauseReason(debuggerBreakpointId);
680             }
681             break;
682         }
683         case JSC::Debugger::PausedForDebuggerStatement:
684             m_breakReason = DebuggerFrontendDispatcher::Reason::DebuggerStatement;
685             m_breakAuxData = nullptr;
686             break;
687         case JSC::Debugger::PausedForException:
688             m_breakReason = DebuggerFrontendDispatcher::Reason::Exception;
689             m_breakAuxData = buildExceptionPauseReason(exceptionOrCaughtValue, injectedScript);
690             break;
691         case JSC::Debugger::PausedAtStatement:
692         case JSC::Debugger::PausedAfterCall:
693         case JSC::Debugger::PausedBeforeReturn:
694         case JSC::Debugger::PausedAtStartOfProgram:
695         case JSC::Debugger::PausedAtEndOfProgram:
696             // Pause was just stepping. Nothing to report.
697             break;
698         case JSC::Debugger::NotPaused:
699             ASSERT_NOT_REACHED();
700             break;
701         }
702     }
703
704     // Set $exception to the exception or caught value.
705     if (!exceptionOrCaughtValue.hasNoValue() && !injectedScript.hasNoValue()) {
706         injectedScript.setExceptionValue(exceptionOrCaughtValue);
707         m_hasExceptionValue = true;
708     }
709
710     m_frontendDispatcher->paused(currentCallFrames(injectedScript), m_breakReason, m_breakAuxData);
711     m_javaScriptPauseScheduled = false;
712
713     if (m_continueToLocationBreakpointID != JSC::noBreakpointID) {
714         m_scriptDebugServer.removeBreakpoint(m_continueToLocationBreakpointID);
715         m_continueToLocationBreakpointID = JSC::noBreakpointID;
716     }
717
718     if (m_listener)
719         m_listener->didPause();
720
721     RefPtr<Stopwatch> stopwatch = m_injectedScriptManager.inspectorEnvironment().executionStopwatch();
722     if (stopwatch && stopwatch->isActive()) {
723         stopwatch->stop();
724         m_didPauseStopwatch = true;
725     }
726 }
727
728 void InspectorDebuggerAgent::breakpointActionSound(int breakpointActionIdentifier)
729 {
730     m_frontendDispatcher->playBreakpointActionSound(breakpointActionIdentifier);
731 }
732
733 void InspectorDebuggerAgent::breakpointActionProbe(JSC::ExecState* scriptState, const ScriptBreakpointAction& action, unsigned batchId, unsigned sampleId, const Deprecated::ScriptValue& sample)
734 {
735     InjectedScript injectedScript = m_injectedScriptManager.injectedScriptFor(scriptState);
736     RefPtr<Protocol::Runtime::RemoteObject> payload = injectedScript.wrapObject(sample, objectGroupForBreakpointAction(action), true);
737     auto result = Protocol::Debugger::ProbeSample::create()
738         .setProbeId(action.identifier)
739         .setBatchId(batchId)
740         .setSampleId(sampleId)
741         .setTimestamp(m_injectedScriptManager.inspectorEnvironment().executionStopwatch()->elapsedTime())
742         .setPayload(payload.release())
743         .release();
744
745     m_frontendDispatcher->didSampleProbe(WTFMove(result));
746 }
747
748 void InspectorDebuggerAgent::didContinue()
749 {
750     if (m_didPauseStopwatch) {
751         m_didPauseStopwatch = false;
752         m_injectedScriptManager.inspectorEnvironment().executionStopwatch()->start();
753     }
754
755     m_pausedScriptState = nullptr;
756     m_currentCallStack = Deprecated::ScriptValue();
757     m_injectedScriptManager.releaseObjectGroup(InspectorDebuggerAgent::backtraceObjectGroup);
758     clearBreakDetails();
759     clearExceptionValue();
760
761     m_frontendDispatcher->resumed();
762 }
763
764 void InspectorDebuggerAgent::breakProgram(DebuggerFrontendDispatcher::Reason breakReason, RefPtr<InspectorObject>&& data)
765 {
766     m_breakReason = breakReason;
767     m_breakAuxData = WTFMove(data);
768     m_scriptDebugServer.breakProgram();
769 }
770
771 void InspectorDebuggerAgent::clearInspectorBreakpointState()
772 {
773     ErrorString dummyError;
774     Vector<String> breakpointIdentifiers;
775     copyKeysToVector(m_breakpointIdentifierToDebugServerBreakpointIDs, breakpointIdentifiers);
776     for (const String& identifier : breakpointIdentifiers)
777         removeBreakpoint(dummyError, identifier);
778
779     m_javaScriptBreakpoints.clear();
780
781     clearDebuggerBreakpointState();
782 }
783
784 void InspectorDebuggerAgent::clearDebuggerBreakpointState()
785 {
786     m_scriptDebugServer.clearBreakpoints();
787
788     m_pausedScriptState = nullptr;
789     m_currentCallStack = Deprecated::ScriptValue();
790     m_scripts.clear();
791     m_breakpointIdentifierToDebugServerBreakpointIDs.clear();
792     m_debuggerBreakpointIdentifierToInspectorBreakpointIdentifier.clear();
793     m_continueToLocationBreakpointID = JSC::noBreakpointID;
794     clearBreakDetails();
795     m_javaScriptPauseScheduled = false;
796     m_hasExceptionValue = false;
797
798     m_scriptDebugServer.continueProgram();
799 }
800
801 void InspectorDebuggerAgent::didClearGlobalObject()
802 {
803     // Clear breakpoints from the debugger, but keep the inspector's model of which
804     // pages have what breakpoints, as the mapping is only sent to DebuggerAgent once.
805     clearDebuggerBreakpointState();
806
807     m_frontendDispatcher->globalObjectCleared();
808 }
809
810 bool InspectorDebuggerAgent::assertPaused(ErrorString& errorString)
811 {
812     if (!m_pausedScriptState) {
813         errorString = ASCIILiteral("Can only perform operation while paused.");
814         return false;
815     }
816
817     return true;
818 }
819
820 void InspectorDebuggerAgent::clearBreakDetails()
821 {
822     m_breakReason = DebuggerFrontendDispatcher::Reason::Other;
823     m_breakAuxData = nullptr;
824 }
825
826 void InspectorDebuggerAgent::clearExceptionValue()
827 {
828     if (m_hasExceptionValue) {
829         m_injectedScriptManager.clearExceptionValue();
830         m_hasExceptionValue = false;
831     }
832 }
833
834 } // namespace Inspector