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