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