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