2 * Copyright (C) 2010, 2013 Apple Inc. All rights reserved.
3 * Copyright (C) 2010-2011 Google Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
15 * its contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
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.
31 #include "InspectorDebuggerAgent.h"
33 #include "ContentSearchUtilities.h"
34 #include "InjectedScript.h"
35 #include "InjectedScriptManager.h"
36 #include "InspectorValues.h"
37 #include "RegularExpression.h"
38 #include "ScriptDebugServer.h"
39 #include "ScriptObject.h"
40 #include "ScriptValue.h"
41 #include <wtf/Stopwatch.h>
42 #include <wtf/text/WTFString.h>
46 const char* InspectorDebuggerAgent::backtraceObjectGroup
= "backtrace";
48 // Objects created and retained by evaluating breakpoint actions are put into object groups
49 // according to the breakpoint action identifier assigned by the frontend. A breakpoint may
50 // have several object groups, and objects from several backend breakpoint action instances may
51 // create objects in the same group.
52 static String
objectGroupForBreakpointAction(const ScriptBreakpointAction
& action
)
54 DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString
, objectGroup
, ("breakpoint-action-", AtomicString::ConstructFromLiteral
));
55 return makeString(objectGroup
, String::number(action
.identifier
));
58 InspectorDebuggerAgent::InspectorDebuggerAgent(InjectedScriptManager
* injectedScriptManager
)
59 : InspectorAgentBase(ASCIILiteral("Debugger"))
60 , m_injectedScriptManager(injectedScriptManager
)
61 , m_continueToLocationBreakpointID(JSC::noBreakpointID
)
63 // FIXME: make breakReason optional so that there was no need to init it with "other".
67 InspectorDebuggerAgent::~InspectorDebuggerAgent()
71 void InspectorDebuggerAgent::didCreateFrontendAndBackend(FrontendChannel
* frontendChannel
, BackendDispatcher
* backendDispatcher
)
73 m_frontendDispatcher
= std::make_unique
<DebuggerFrontendDispatcher
>(frontendChannel
);
74 m_backendDispatcher
= DebuggerBackendDispatcher::create(backendDispatcher
, this);
77 void InspectorDebuggerAgent::willDestroyFrontendAndBackend(DisconnectReason reason
)
79 m_frontendDispatcher
= nullptr;
80 m_backendDispatcher
= nullptr;
82 bool skipRecompile
= reason
== DisconnectReason::InspectedTargetDestroyed
;
83 disable(skipRecompile
);
86 void InspectorDebuggerAgent::enable()
91 scriptDebugServer().setBreakpointsActivated(true);
92 startListeningScriptDebugServer();
95 m_listener
->debuggerWasEnabled();
100 void InspectorDebuggerAgent::disable(bool isBeingDestroyed
)
105 stopListeningScriptDebugServer(isBeingDestroyed
);
106 clearInspectorBreakpointState();
108 ASSERT(m_javaScriptBreakpoints
.isEmpty());
111 m_listener
->debuggerWasDisabled();
116 void InspectorDebuggerAgent::enable(ErrorString
&)
121 void InspectorDebuggerAgent::disable(ErrorString
&)
126 void InspectorDebuggerAgent::setBreakpointsActive(ErrorString
&, bool active
)
129 scriptDebugServer().activateBreakpoints();
131 scriptDebugServer().deactivateBreakpoints();
134 bool InspectorDebuggerAgent::isPaused()
136 return scriptDebugServer().isPaused();
139 static RefPtr
<InspectorObject
> buildAssertPauseReason(const String
& message
)
141 auto reason
= Inspector::Protocol::Debugger::AssertPauseReason::create().release();
142 if (!message
.isNull())
143 reason
->setMessage(message
);
144 return reason
->openAccessors();
147 static RefPtr
<InspectorObject
> buildCSPViolationPauseReason(const String
& directiveText
)
149 auto reason
= Inspector::Protocol::Debugger::CSPViolationPauseReason::create()
150 .setDirective(directiveText
)
152 return reason
->openAccessors();
155 RefPtr
<InspectorObject
> InspectorDebuggerAgent::buildBreakpointPauseReason(JSC::BreakpointID debuggerBreakpointIdentifier
)
157 ASSERT(debuggerBreakpointIdentifier
!= JSC::noBreakpointID
);
158 auto it
= m_debuggerBreakpointIdentifierToInspectorBreakpointIdentifier
.find(debuggerBreakpointIdentifier
);
159 if (it
== m_debuggerBreakpointIdentifierToInspectorBreakpointIdentifier
.end())
162 auto reason
= Inspector::Protocol::Debugger::BreakpointPauseReason::create()
163 .setBreakpointId(it
->value
)
165 return reason
->openAccessors();
168 RefPtr
<InspectorObject
> InspectorDebuggerAgent::buildExceptionPauseReason(const Deprecated::ScriptValue
& exception
, const InjectedScript
& injectedScript
)
170 ASSERT(!exception
.hasNoValue());
171 if (exception
.hasNoValue())
174 ASSERT(!injectedScript
.hasNoValue());
175 if (injectedScript
.hasNoValue())
178 return injectedScript
.wrapObject(exception
, InspectorDebuggerAgent::backtraceObjectGroup
)->openAccessors();
181 void InspectorDebuggerAgent::handleConsoleAssert(const String
& message
)
183 if (scriptDebugServer().pauseOnExceptionsState() != JSC::Debugger::DontPauseOnExceptions
)
184 breakProgram(DebuggerFrontendDispatcher::Reason::Assert
, buildAssertPauseReason(message
));
187 static Ref
<InspectorObject
> buildObjectForBreakpointCookie(const String
& url
, int lineNumber
, int columnNumber
, const String
& condition
, RefPtr
<InspectorArray
>& actions
, bool isRegex
, bool autoContinue
)
189 Ref
<InspectorObject
> breakpointObject
= InspectorObject::create();
190 breakpointObject
->setString(ASCIILiteral("url"), url
);
191 breakpointObject
->setInteger(ASCIILiteral("lineNumber"), lineNumber
);
192 breakpointObject
->setInteger(ASCIILiteral("columnNumber"), columnNumber
);
193 breakpointObject
->setString(ASCIILiteral("condition"), condition
);
194 breakpointObject
->setBoolean(ASCIILiteral("isRegex"), isRegex
);
195 breakpointObject
->setBoolean(ASCIILiteral("autoContinue"), autoContinue
);
198 breakpointObject
->setArray(ASCIILiteral("actions"), actions
);
200 return breakpointObject
;
203 static bool matches(const String
& url
, const String
& pattern
, bool isRegex
)
206 JSC::Yarr::RegularExpression
regex(pattern
, TextCaseSensitive
);
207 return regex
.match(url
) != -1;
209 return url
== pattern
;
212 static bool breakpointActionTypeForString(const String
& typeString
, ScriptBreakpointActionType
* output
)
214 if (typeString
== Inspector::Protocol::getEnumConstantValue(Inspector::Protocol::Debugger::BreakpointAction::Type::Log
)) {
215 *output
= ScriptBreakpointActionTypeLog
;
218 if (typeString
== Inspector::Protocol::getEnumConstantValue(Inspector::Protocol::Debugger::BreakpointAction::Type::Evaluate
)) {
219 *output
= ScriptBreakpointActionTypeEvaluate
;
222 if (typeString
== Inspector::Protocol::getEnumConstantValue(Inspector::Protocol::Debugger::BreakpointAction::Type::Sound
)) {
223 *output
= ScriptBreakpointActionTypeSound
;
226 if (typeString
== Inspector::Protocol::getEnumConstantValue(Inspector::Protocol::Debugger::BreakpointAction::Type::Probe
)) {
227 *output
= ScriptBreakpointActionTypeProbe
;
234 bool InspectorDebuggerAgent::breakpointActionsFromProtocol(ErrorString
& errorString
, RefPtr
<InspectorArray
>& actions
, BreakpointActions
* result
)
239 unsigned actionsLength
= actions
->length();
243 result
->reserveCapacity(actionsLength
);
244 for (unsigned i
= 0; i
< actionsLength
; ++i
) {
245 RefPtr
<InspectorValue
> value
= actions
->get(i
);
246 RefPtr
<InspectorObject
> object
;
247 if (!value
->asObject(object
)) {
248 errorString
= ASCIILiteral("BreakpointAction of incorrect type, expected object");
253 if (!object
->getString(ASCIILiteral("type"), typeString
)) {
254 errorString
= ASCIILiteral("BreakpointAction had type missing");
258 ScriptBreakpointActionType type
;
259 if (!breakpointActionTypeForString(typeString
, &type
)) {
260 errorString
= ASCIILiteral("BreakpointAction had unknown type");
264 // Specifying an identifier is optional. They are used to correlate probe samples
265 // in the frontend across multiple backend probe actions and segregate object groups.
267 object
->getInteger(ASCIILiteral("id"), identifier
);
270 object
->getString(ASCIILiteral("data"), data
);
272 result
->append(ScriptBreakpointAction(type
, identifier
, data
));
278 void InspectorDebuggerAgent::setBreakpointByUrl(ErrorString
& errorString
, int lineNumber
, const String
* const optionalURL
, const String
* const optionalURLRegex
, const int* const optionalColumnNumber
, const InspectorObject
* options
, Inspector::Protocol::Debugger::BreakpointId
* outBreakpointIdentifier
, RefPtr
<Inspector::Protocol::Array
<Inspector::Protocol::Debugger::Location
>>& locations
)
280 locations
= Inspector::Protocol::Array
<Inspector::Protocol::Debugger::Location
>::create();
281 if (!optionalURL
== !optionalURLRegex
) {
282 errorString
= ASCIILiteral("Either url or urlRegex must be specified.");
286 String url
= optionalURL
? *optionalURL
: *optionalURLRegex
;
287 int columnNumber
= optionalColumnNumber
? *optionalColumnNumber
: 0;
288 bool isRegex
= optionalURLRegex
;
290 String breakpointIdentifier
= (isRegex
? "/" + url
+ "/" : url
) + ':' + String::number(lineNumber
) + ':' + String::number(columnNumber
);
291 if (m_javaScriptBreakpoints
.contains(breakpointIdentifier
)) {
292 errorString
= ASCIILiteral("Breakpoint at specified location already exists.");
296 String condition
= emptyString();
297 bool autoContinue
= false;
298 RefPtr
<InspectorArray
> actions
;
300 options
->getString(ASCIILiteral("condition"), condition
);
301 options
->getBoolean(ASCIILiteral("autoContinue"), autoContinue
);
302 options
->getArray(ASCIILiteral("actions"), actions
);
305 BreakpointActions breakpointActions
;
306 if (!breakpointActionsFromProtocol(errorString
, actions
, &breakpointActions
))
309 m_javaScriptBreakpoints
.set(breakpointIdentifier
, buildObjectForBreakpointCookie(url
, lineNumber
, columnNumber
, condition
, actions
, isRegex
, autoContinue
));
311 ScriptBreakpoint
breakpoint(lineNumber
, columnNumber
, condition
, breakpointActions
, autoContinue
);
312 for (ScriptsMap::iterator it
= m_scripts
.begin(); it
!= m_scripts
.end(); ++it
) {
313 String scriptURL
= !it
->value
.sourceURL
.isEmpty() ? it
->value
.sourceURL
: it
->value
.url
;
314 if (!matches(scriptURL
, url
, isRegex
))
317 RefPtr
<Inspector::Protocol::Debugger::Location
> location
= resolveBreakpoint(breakpointIdentifier
, it
->key
, breakpoint
);
319 locations
->addItem(WTF::move(location
));
321 *outBreakpointIdentifier
= breakpointIdentifier
;
324 static bool parseLocation(ErrorString
& errorString
, const InspectorObject
& location
, JSC::SourceID
& sourceID
, unsigned& lineNumber
, unsigned& columnNumber
)
327 if (!location
.getString(ASCIILiteral("scriptId"), scriptIDStr
) || !location
.getInteger(ASCIILiteral("lineNumber"), lineNumber
)) {
328 sourceID
= JSC::noSourceID
;
329 errorString
= ASCIILiteral("scriptId and lineNumber are required.");
333 sourceID
= scriptIDStr
.toIntPtr();
335 location
.getInteger(ASCIILiteral("columnNumber"), columnNumber
);
339 void InspectorDebuggerAgent::setBreakpoint(ErrorString
& errorString
, const InspectorObject
& location
, const InspectorObject
* options
, Inspector::Protocol::Debugger::BreakpointId
* outBreakpointIdentifier
, RefPtr
<Inspector::Protocol::Debugger::Location
>& actualLocation
)
341 JSC::SourceID sourceID
;
343 unsigned columnNumber
;
344 if (!parseLocation(errorString
, location
, sourceID
, lineNumber
, columnNumber
))
347 String condition
= emptyString();
348 bool autoContinue
= false;
349 RefPtr
<InspectorArray
> actions
;
351 options
->getString(ASCIILiteral("condition"), condition
);
352 options
->getBoolean(ASCIILiteral("autoContinue"), autoContinue
);
353 options
->getArray(ASCIILiteral("actions"), actions
);
356 BreakpointActions breakpointActions
;
357 if (!breakpointActionsFromProtocol(errorString
, actions
, &breakpointActions
))
360 String breakpointIdentifier
= String::number(sourceID
) + ':' + String::number(lineNumber
) + ':' + String::number(columnNumber
);
361 if (m_breakpointIdentifierToDebugServerBreakpointIDs
.find(breakpointIdentifier
) != m_breakpointIdentifierToDebugServerBreakpointIDs
.end()) {
362 errorString
= ASCIILiteral("Breakpoint at specified location already exists.");
366 ScriptBreakpoint
breakpoint(lineNumber
, columnNumber
, condition
, breakpointActions
, autoContinue
);
367 actualLocation
= resolveBreakpoint(breakpointIdentifier
, sourceID
, breakpoint
);
368 if (!actualLocation
) {
369 errorString
= ASCIILiteral("Could not resolve breakpoint");
373 *outBreakpointIdentifier
= breakpointIdentifier
;
376 void InspectorDebuggerAgent::removeBreakpoint(ErrorString
&, const String
& breakpointIdentifier
)
378 m_javaScriptBreakpoints
.remove(breakpointIdentifier
);
380 for (JSC::BreakpointID breakpointID
: m_breakpointIdentifierToDebugServerBreakpointIDs
.take(breakpointIdentifier
)) {
381 m_debuggerBreakpointIdentifierToInspectorBreakpointIdentifier
.remove(breakpointID
);
383 const BreakpointActions
& breakpointActions
= scriptDebugServer().getActionsForBreakpoint(breakpointID
);
384 for (auto& action
: breakpointActions
)
385 m_injectedScriptManager
->releaseObjectGroup(objectGroupForBreakpointAction(action
));
387 scriptDebugServer().removeBreakpoint(breakpointID
);
391 void InspectorDebuggerAgent::continueToLocation(ErrorString
& errorString
, const InspectorObject
& location
)
393 if (m_continueToLocationBreakpointID
!= JSC::noBreakpointID
) {
394 scriptDebugServer().removeBreakpoint(m_continueToLocationBreakpointID
);
395 m_continueToLocationBreakpointID
= JSC::noBreakpointID
;
398 JSC::SourceID sourceID
;
400 unsigned columnNumber
;
401 if (!parseLocation(errorString
, location
, sourceID
, lineNumber
, columnNumber
))
404 ScriptBreakpoint
breakpoint(lineNumber
, columnNumber
, "", false);
405 m_continueToLocationBreakpointID
= scriptDebugServer().setBreakpoint(sourceID
, breakpoint
, &lineNumber
, &columnNumber
);
409 RefPtr
<Inspector::Protocol::Debugger::Location
> InspectorDebuggerAgent::resolveBreakpoint(const String
& breakpointIdentifier
, JSC::SourceID sourceID
, const ScriptBreakpoint
& breakpoint
)
411 ScriptsMap::iterator scriptIterator
= m_scripts
.find(sourceID
);
412 if (scriptIterator
== m_scripts
.end())
414 Script
& script
= scriptIterator
->value
;
415 if (breakpoint
.lineNumber
< script
.startLine
|| script
.endLine
< breakpoint
.lineNumber
)
418 unsigned actualLineNumber
;
419 unsigned actualColumnNumber
;
420 JSC::BreakpointID debugServerBreakpointID
= scriptDebugServer().setBreakpoint(sourceID
, breakpoint
, &actualLineNumber
, &actualColumnNumber
);
421 if (debugServerBreakpointID
== JSC::noBreakpointID
)
424 BreakpointIdentifierToDebugServerBreakpointIDsMap::iterator debugServerBreakpointIDsIterator
= m_breakpointIdentifierToDebugServerBreakpointIDs
.find(breakpointIdentifier
);
425 if (debugServerBreakpointIDsIterator
== m_breakpointIdentifierToDebugServerBreakpointIDs
.end())
426 debugServerBreakpointIDsIterator
= m_breakpointIdentifierToDebugServerBreakpointIDs
.set(breakpointIdentifier
, Vector
<JSC::BreakpointID
>()).iterator
;
427 debugServerBreakpointIDsIterator
->value
.append(debugServerBreakpointID
);
429 m_debuggerBreakpointIdentifierToInspectorBreakpointIdentifier
.set(debugServerBreakpointID
, breakpointIdentifier
);
431 auto location
= Inspector::Protocol::Debugger::Location::create()
432 .setScriptId(String::number(sourceID
))
433 .setLineNumber(actualLineNumber
)
435 location
->setColumnNumber(actualColumnNumber
);
436 return WTF::move(location
);
439 void InspectorDebuggerAgent::searchInContent(ErrorString
& error
, const String
& scriptIDStr
, const String
& query
, const bool* optionalCaseSensitive
, const bool* optionalIsRegex
, RefPtr
<Inspector::Protocol::Array
<Inspector::Protocol::GenericTypes::SearchMatch
>>& results
)
441 JSC::SourceID sourceID
= scriptIDStr
.toIntPtr();
442 auto it
= m_scripts
.find(sourceID
);
443 if (it
== m_scripts
.end()) {
444 error
= ASCIILiteral("No script for id: ") + scriptIDStr
;
448 bool isRegex
= optionalIsRegex
? *optionalIsRegex
: false;
449 bool caseSensitive
= optionalCaseSensitive
? *optionalCaseSensitive
: false;
450 results
= ContentSearchUtilities::searchInTextByLines(it
->value
.source
, query
, caseSensitive
, isRegex
);
453 void InspectorDebuggerAgent::getScriptSource(ErrorString
& error
, const String
& scriptIDStr
, String
* scriptSource
)
455 JSC::SourceID sourceID
= scriptIDStr
.toIntPtr();
456 ScriptsMap::iterator it
= m_scripts
.find(sourceID
);
457 if (it
!= m_scripts
.end())
458 *scriptSource
= it
->value
.source
;
460 error
= ASCIILiteral("No script for id: ") + scriptIDStr
;
463 void InspectorDebuggerAgent::getFunctionDetails(ErrorString
& errorString
, const String
& functionId
, RefPtr
<Inspector::Protocol::Debugger::FunctionDetails
>& details
)
465 InjectedScript injectedScript
= m_injectedScriptManager
->injectedScriptForObjectId(functionId
);
466 if (injectedScript
.hasNoValue()) {
467 errorString
= ASCIILiteral("Function object id is obsolete");
471 injectedScript
.getFunctionDetails(errorString
, functionId
, &details
);
474 void InspectorDebuggerAgent::schedulePauseOnNextStatement(DebuggerFrontendDispatcher::Reason breakReason
, RefPtr
<InspectorObject
>&& data
)
476 if (m_javaScriptPauseScheduled
)
479 m_breakReason
= breakReason
;
480 m_breakAuxData
= WTF::move(data
);
481 scriptDebugServer().setPauseOnNextStatement(true);
484 void InspectorDebuggerAgent::cancelPauseOnNextStatement()
486 if (m_javaScriptPauseScheduled
)
490 scriptDebugServer().setPauseOnNextStatement(false);
493 void InspectorDebuggerAgent::pause(ErrorString
&)
495 schedulePauseOnNextStatement(DebuggerFrontendDispatcher::Reason::PauseOnNextStatement
, nullptr);
497 m_javaScriptPauseScheduled
= true;
500 void InspectorDebuggerAgent::resume(ErrorString
& errorString
)
502 if (!assertPaused(errorString
))
505 scriptDebugServer().continueProgram();
508 void InspectorDebuggerAgent::stepOver(ErrorString
& errorString
)
510 if (!assertPaused(errorString
))
513 scriptDebugServer().stepOverStatement();
516 void InspectorDebuggerAgent::stepInto(ErrorString
& errorString
)
518 if (!assertPaused(errorString
))
521 scriptDebugServer().stepIntoStatement();
524 m_listener
->stepInto();
527 void InspectorDebuggerAgent::stepOut(ErrorString
& errorString
)
529 if (!assertPaused(errorString
))
532 scriptDebugServer().stepOutOfFunction();
535 void InspectorDebuggerAgent::setPauseOnExceptions(ErrorString
& errorString
, const String
& stringPauseState
)
537 JSC::Debugger::PauseOnExceptionsState pauseState
;
538 if (stringPauseState
== "none")
539 pauseState
= JSC::Debugger::DontPauseOnExceptions
;
540 else if (stringPauseState
== "all")
541 pauseState
= JSC::Debugger::PauseOnAllExceptions
;
542 else if (stringPauseState
== "uncaught")
543 pauseState
= JSC::Debugger::PauseOnUncaughtExceptions
;
545 errorString
= ASCIILiteral("Unknown pause on exceptions mode: ") + stringPauseState
;
549 scriptDebugServer().setPauseOnExceptionsState(static_cast<JSC::Debugger::PauseOnExceptionsState
>(pauseState
));
550 if (scriptDebugServer().pauseOnExceptionsState() != pauseState
)
551 errorString
= ASCIILiteral("Internal error. Could not change pause on exceptions state");
554 void InspectorDebuggerAgent::evaluateOnCallFrame(ErrorString
& errorString
, const String
& callFrameId
, const String
& expression
, const String
* const objectGroup
, const bool* const includeCommandLineAPI
, const bool* const doNotPauseOnExceptionsAndMuteConsole
, const bool* const returnByValue
, const bool* generatePreview
, const bool* saveResult
, RefPtr
<Inspector::Protocol::Runtime::RemoteObject
>& result
, Inspector::Protocol::OptOutput
<bool>* wasThrown
, Inspector::Protocol::OptOutput
<int>* savedResultIndex
)
556 InjectedScript injectedScript
= m_injectedScriptManager
->injectedScriptForObjectId(callFrameId
);
557 if (injectedScript
.hasNoValue()) {
558 errorString
= ASCIILiteral("Inspected frame has gone");
562 JSC::Debugger::PauseOnExceptionsState previousPauseOnExceptionsState
= scriptDebugServer().pauseOnExceptionsState();
563 if (doNotPauseOnExceptionsAndMuteConsole
? *doNotPauseOnExceptionsAndMuteConsole
: false) {
564 if (previousPauseOnExceptionsState
!= JSC::Debugger::DontPauseOnExceptions
)
565 scriptDebugServer().setPauseOnExceptionsState(JSC::Debugger::DontPauseOnExceptions
);
569 injectedScript
.evaluateOnCallFrame(errorString
, m_currentCallStack
, callFrameId
, expression
, objectGroup
? *objectGroup
: "", includeCommandLineAPI
? *includeCommandLineAPI
: false, returnByValue
? *returnByValue
: false, generatePreview
? *generatePreview
: false, saveResult
? *saveResult
: false, &result
, wasThrown
, savedResultIndex
);
571 if (doNotPauseOnExceptionsAndMuteConsole
? *doNotPauseOnExceptionsAndMuteConsole
: false) {
573 if (scriptDebugServer().pauseOnExceptionsState() != previousPauseOnExceptionsState
)
574 scriptDebugServer().setPauseOnExceptionsState(previousPauseOnExceptionsState
);
578 void InspectorDebuggerAgent::setOverlayMessage(ErrorString
&, const String
*)
582 void InspectorDebuggerAgent::scriptExecutionBlockedByCSP(const String
& directiveText
)
584 if (scriptDebugServer().pauseOnExceptionsState() != JSC::Debugger::DontPauseOnExceptions
)
585 breakProgram(DebuggerFrontendDispatcher::Reason::CSPViolation
, buildCSPViolationPauseReason(directiveText
));
588 Ref
<Inspector::Protocol::Array
<Inspector::Protocol::Debugger::CallFrame
>> InspectorDebuggerAgent::currentCallFrames(InjectedScript injectedScript
)
590 ASSERT(!injectedScript
.hasNoValue());
591 if (injectedScript
.hasNoValue())
592 return Inspector::Protocol::Array
<Inspector::Protocol::Debugger::CallFrame
>::create();
594 return injectedScript
.wrapCallFrames(m_currentCallStack
);
597 String
InspectorDebuggerAgent::sourceMapURLForScript(const Script
& script
)
599 return ContentSearchUtilities::findScriptSourceMapURL(script
.source
);
602 void InspectorDebuggerAgent::didParseSource(JSC::SourceID sourceID
, const Script
& inScript
)
604 Script script
= inScript
;
605 if (script
.startLine
<= 0 && !script
.startColumn
)
606 script
.sourceURL
= ContentSearchUtilities::findScriptSourceURL(script
.source
);
607 script
.sourceMappingURL
= sourceMapURLForScript(script
);
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
);
617 m_scripts
.set(sourceID
, script
);
619 if (scriptURL
.isEmpty())
622 for (auto it
= m_javaScriptBreakpoints
.begin(), end
= m_javaScriptBreakpoints
.end(); it
!= end
; ++it
) {
623 RefPtr
<InspectorObject
> breakpointObject
;
624 if (!it
->value
->asObject(breakpointObject
))
628 breakpointObject
->getBoolean(ASCIILiteral("isRegex"), isRegex
);
630 breakpointObject
->getString(ASCIILiteral("url"), url
);
631 if (!matches(scriptURL
, url
, isRegex
))
634 ScriptBreakpoint breakpoint
;
635 breakpointObject
->getInteger(ASCIILiteral("lineNumber"), breakpoint
.lineNumber
);
636 breakpointObject
->getInteger(ASCIILiteral("columnNumber"), breakpoint
.columnNumber
);
637 breakpointObject
->getString(ASCIILiteral("condition"), breakpoint
.condition
);
638 breakpointObject
->getBoolean(ASCIILiteral("autoContinue"), breakpoint
.autoContinue
);
639 ErrorString errorString
;
640 RefPtr
<InspectorArray
> actions
;
641 breakpointObject
->getArray(ASCIILiteral("actions"), actions
);
642 if (!breakpointActionsFromProtocol(errorString
, actions
, &breakpoint
.actions
)) {
643 ASSERT_NOT_REACHED();
647 RefPtr
<Inspector::Protocol::Debugger::Location
> location
= resolveBreakpoint(it
->key
, sourceID
, breakpoint
);
649 m_frontendDispatcher
->breakpointResolved(it
->key
, location
);
653 void InspectorDebuggerAgent::failedToParseSource(const String
& url
, const String
& data
, int firstLine
, int errorLine
, const String
& errorMessage
)
655 m_frontendDispatcher
->scriptFailedToParse(url
, data
, firstLine
, errorLine
, errorMessage
);
658 void InspectorDebuggerAgent::didPause(JSC::ExecState
* scriptState
, const Deprecated::ScriptValue
& callFrames
, const Deprecated::ScriptValue
& exceptionOrCaughtValue
)
660 ASSERT(scriptState
&& !m_pausedScriptState
);
661 m_pausedScriptState
= scriptState
;
662 m_currentCallStack
= callFrames
;
664 InjectedScript injectedScript
= m_injectedScriptManager
->injectedScriptFor(scriptState
);
666 // If a high level pause pause reason is not already set, try to infer a reason from the debugger.
667 if (m_breakReason
== DebuggerFrontendDispatcher::Reason::Other
) {
668 switch (scriptDebugServer().reasonForPause()) {
669 case JSC::Debugger::PausedForBreakpoint
: {
670 JSC::BreakpointID debuggerBreakpointId
= scriptDebugServer().pausingBreakpointID();
671 if (debuggerBreakpointId
!= m_continueToLocationBreakpointID
) {
672 m_breakReason
= DebuggerFrontendDispatcher::Reason::Breakpoint
;
673 m_breakAuxData
= buildBreakpointPauseReason(debuggerBreakpointId
);
677 case JSC::Debugger::PausedForDebuggerStatement
:
678 m_breakReason
= DebuggerFrontendDispatcher::Reason::DebuggerStatement
;
679 m_breakAuxData
= nullptr;
681 case JSC::Debugger::PausedForException
:
682 m_breakReason
= DebuggerFrontendDispatcher::Reason::Exception
;
683 m_breakAuxData
= buildExceptionPauseReason(exceptionOrCaughtValue
, injectedScript
);
685 case JSC::Debugger::PausedAtStatement
:
686 case JSC::Debugger::PausedAfterCall
:
687 case JSC::Debugger::PausedBeforeReturn
:
688 case JSC::Debugger::PausedAtStartOfProgram
:
689 case JSC::Debugger::PausedAtEndOfProgram
:
690 // Pause was just stepping. Nothing to report.
692 case JSC::Debugger::NotPaused
:
693 ASSERT_NOT_REACHED();
698 // Set $exception to the exception or caught value.
699 if (!exceptionOrCaughtValue
.hasNoValue() && !injectedScript
.hasNoValue()) {
700 injectedScript
.setExceptionValue(exceptionOrCaughtValue
);
701 m_hasExceptionValue
= true;
704 m_frontendDispatcher
->paused(currentCallFrames(injectedScript
), m_breakReason
, m_breakAuxData
);
705 m_javaScriptPauseScheduled
= false;
707 if (m_continueToLocationBreakpointID
!= JSC::noBreakpointID
) {
708 scriptDebugServer().removeBreakpoint(m_continueToLocationBreakpointID
);
709 m_continueToLocationBreakpointID
= JSC::noBreakpointID
;
713 m_listener
->didPause();
715 RefPtr
<Stopwatch
> stopwatch
= m_injectedScriptManager
->inspectorEnvironment().executionStopwatch();
716 if (stopwatch
&& stopwatch
->isActive()) {
718 m_didPauseStopwatch
= true;
722 void InspectorDebuggerAgent::breakpointActionSound(int breakpointActionIdentifier
)
724 m_frontendDispatcher
->playBreakpointActionSound(breakpointActionIdentifier
);
727 void InspectorDebuggerAgent::breakpointActionProbe(JSC::ExecState
* scriptState
, const ScriptBreakpointAction
& action
, unsigned batchId
, unsigned sampleId
, const Deprecated::ScriptValue
& sample
)
729 InjectedScript injectedScript
= m_injectedScriptManager
->injectedScriptFor(scriptState
);
730 RefPtr
<Protocol::Runtime::RemoteObject
> payload
= injectedScript
.wrapObject(sample
, objectGroupForBreakpointAction(action
), true);
731 auto result
= Protocol::Debugger::ProbeSample::create()
732 .setProbeId(action
.identifier
)
734 .setSampleId(sampleId
)
735 .setTimestamp(m_injectedScriptManager
->inspectorEnvironment().executionStopwatch()->elapsedTime())
736 .setPayload(payload
.release())
739 m_frontendDispatcher
->didSampleProbe(WTF::move(result
));
742 void InspectorDebuggerAgent::didContinue()
744 if (m_didPauseStopwatch
) {
745 m_didPauseStopwatch
= false;
746 m_injectedScriptManager
->inspectorEnvironment().executionStopwatch()->start();
749 m_pausedScriptState
= nullptr;
750 m_currentCallStack
= Deprecated::ScriptValue();
751 m_injectedScriptManager
->releaseObjectGroup(InspectorDebuggerAgent::backtraceObjectGroup
);
753 clearExceptionValue();
755 m_frontendDispatcher
->resumed();
758 void InspectorDebuggerAgent::breakProgram(DebuggerFrontendDispatcher::Reason breakReason
, RefPtr
<InspectorObject
>&& data
)
760 m_breakReason
= breakReason
;
761 m_breakAuxData
= WTF::move(data
);
762 scriptDebugServer().breakProgram();
765 void InspectorDebuggerAgent::clearInspectorBreakpointState()
767 ErrorString dummyError
;
768 Vector
<String
> breakpointIdentifiers
;
769 copyKeysToVector(m_breakpointIdentifierToDebugServerBreakpointIDs
, breakpointIdentifiers
);
770 for (const String
& identifier
: breakpointIdentifiers
)
771 removeBreakpoint(dummyError
, identifier
);
773 m_javaScriptBreakpoints
.clear();
775 clearDebuggerBreakpointState();
778 void InspectorDebuggerAgent::clearDebuggerBreakpointState()
780 scriptDebugServer().clearBreakpoints();
782 m_pausedScriptState
= nullptr;
783 m_currentCallStack
= Deprecated::ScriptValue();
785 m_breakpointIdentifierToDebugServerBreakpointIDs
.clear();
786 m_debuggerBreakpointIdentifierToInspectorBreakpointIdentifier
.clear();
787 m_continueToLocationBreakpointID
= JSC::noBreakpointID
;
789 m_javaScriptPauseScheduled
= false;
790 m_hasExceptionValue
= false;
792 scriptDebugServer().continueProgram();
795 void InspectorDebuggerAgent::didClearGlobalObject()
797 // Clear breakpoints from the debugger, but keep the inspector's model of which
798 // pages have what breakpoints, as the mapping is only sent to DebuggerAgent once.
799 clearDebuggerBreakpointState();
801 if (m_frontendDispatcher
)
802 m_frontendDispatcher
->globalObjectCleared();
805 bool InspectorDebuggerAgent::assertPaused(ErrorString
& errorString
)
807 if (!m_pausedScriptState
) {
808 errorString
= ASCIILiteral("Can only perform operation while paused.");
815 void InspectorDebuggerAgent::clearBreakDetails()
817 m_breakReason
= DebuggerFrontendDispatcher::Reason::Other
;
818 m_breakAuxData
= nullptr;
821 void InspectorDebuggerAgent::clearExceptionValue()
823 if (m_hasExceptionValue
) {
824 m_injectedScriptManager
->clearExceptionValue();
825 m_hasExceptionValue
= false;
829 } // namespace Inspector