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