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