]> git.saurik.com Git - apple/javascriptcore.git/blob - inspector/agents/InspectorDebuggerAgent.cpp
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / inspector / agents / InspectorDebuggerAgent.cpp
1 /*
2 * Copyright (C) 2010, 2013 Apple Inc. All rights reserved.
3 * Copyright (C) 2010-2011 Google Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
15 * its contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "config.h"
31 #include "InspectorDebuggerAgent.h"
32
33 #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>
43
44 namespace Inspector {
45
46 const char* InspectorDebuggerAgent::backtraceObjectGroup = "backtrace";
47
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)
53 {
54 DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, objectGroup, ("breakpoint-action-", AtomicString::ConstructFromLiteral));
55 return makeString(objectGroup, String::number(action.identifier));
56 }
57
58 InspectorDebuggerAgent::InspectorDebuggerAgent(InjectedScriptManager* injectedScriptManager)
59 : InspectorAgentBase(ASCIILiteral("Debugger"))
60 , m_injectedScriptManager(injectedScriptManager)
61 , m_continueToLocationBreakpointID(JSC::noBreakpointID)
62 {
63 // FIXME: make breakReason optional so that there was no need to init it with "other".
64 clearBreakDetails();
65 }
66
67 InspectorDebuggerAgent::~InspectorDebuggerAgent()
68 {
69 }
70
71 void InspectorDebuggerAgent::didCreateFrontendAndBackend(FrontendChannel* frontendChannel, BackendDispatcher* backendDispatcher)
72 {
73 m_frontendDispatcher = std::make_unique<DebuggerFrontendDispatcher>(frontendChannel);
74 m_backendDispatcher = DebuggerBackendDispatcher::create(backendDispatcher, this);
75 }
76
77 void InspectorDebuggerAgent::willDestroyFrontendAndBackend(DisconnectReason reason)
78 {
79 m_frontendDispatcher = nullptr;
80 m_backendDispatcher = nullptr;
81
82 bool skipRecompile = reason == DisconnectReason::InspectedTargetDestroyed;
83 disable(skipRecompile);
84 }
85
86 void InspectorDebuggerAgent::enable()
87 {
88 if (m_enabled)
89 return;
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 if (!m_enabled)
103 return;
104
105 stopListeningScriptDebugServer(isBeingDestroyed);
106 clearInspectorBreakpointState();
107
108 ASSERT(m_javaScriptBreakpoints.isEmpty());
109
110 if (m_listener)
111 m_listener->debuggerWasDisabled();
112
113 m_enabled = false;
114 }
115
116 void InspectorDebuggerAgent::enable(ErrorString&)
117 {
118 enable();
119 }
120
121 void InspectorDebuggerAgent::disable(ErrorString&)
122 {
123 disable(false);
124 }
125
126 void InspectorDebuggerAgent::setBreakpointsActive(ErrorString&, bool active)
127 {
128 if (active)
129 scriptDebugServer().activateBreakpoints();
130 else
131 scriptDebugServer().deactivateBreakpoints();
132 }
133
134 bool InspectorDebuggerAgent::isPaused()
135 {
136 return scriptDebugServer().isPaused();
137 }
138
139 static RefPtr<InspectorObject> buildAssertPauseReason(const String& message)
140 {
141 auto reason = Inspector::Protocol::Debugger::AssertPauseReason::create().release();
142 if (!message.isNull())
143 reason->setMessage(message);
144 return reason->openAccessors();
145 }
146
147 static RefPtr<InspectorObject> buildCSPViolationPauseReason(const String& directiveText)
148 {
149 auto reason = Inspector::Protocol::Debugger::CSPViolationPauseReason::create()
150 .setDirective(directiveText)
151 .release();
152 return reason->openAccessors();
153 }
154
155 RefPtr<InspectorObject> InspectorDebuggerAgent::buildBreakpointPauseReason(JSC::BreakpointID debuggerBreakpointIdentifier)
156 {
157 ASSERT(debuggerBreakpointIdentifier != JSC::noBreakpointID);
158 auto it = m_debuggerBreakpointIdentifierToInspectorBreakpointIdentifier.find(debuggerBreakpointIdentifier);
159 if (it == m_debuggerBreakpointIdentifierToInspectorBreakpointIdentifier.end())
160 return nullptr;
161
162 auto reason = Inspector::Protocol::Debugger::BreakpointPauseReason::create()
163 .setBreakpointId(it->value)
164 .release();
165 return reason->openAccessors();
166 }
167
168 RefPtr<InspectorObject> InspectorDebuggerAgent::buildExceptionPauseReason(const Deprecated::ScriptValue& exception, const InjectedScript& injectedScript)
169 {
170 ASSERT(!exception.hasNoValue());
171 if (exception.hasNoValue())
172 return nullptr;
173
174 ASSERT(!injectedScript.hasNoValue());
175 if (injectedScript.hasNoValue())
176 return nullptr;
177
178 return injectedScript.wrapObject(exception, InspectorDebuggerAgent::backtraceObjectGroup)->openAccessors();
179 }
180
181 void InspectorDebuggerAgent::handleConsoleAssert(const String& message)
182 {
183 if (scriptDebugServer().pauseOnExceptionsState() != JSC::Debugger::DontPauseOnExceptions)
184 breakProgram(DebuggerFrontendDispatcher::Reason::Assert, buildAssertPauseReason(message));
185 }
186
187 static Ref<InspectorObject> buildObjectForBreakpointCookie(const String& url, int lineNumber, int columnNumber, const String& condition, RefPtr<InspectorArray>& actions, bool isRegex, bool autoContinue)
188 {
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);
196
197 if (actions)
198 breakpointObject->setArray(ASCIILiteral("actions"), actions);
199
200 return breakpointObject;
201 }
202
203 static bool matches(const String& url, const String& pattern, bool isRegex)
204 {
205 if (isRegex) {
206 JSC::Yarr::RegularExpression regex(pattern, TextCaseSensitive);
207 return regex.match(url) != -1;
208 }
209 return url == pattern;
210 }
211
212 static bool breakpointActionTypeForString(const String& typeString, ScriptBreakpointActionType* output)
213 {
214 if (typeString == Inspector::Protocol::getEnumConstantValue(Inspector::Protocol::Debugger::BreakpointAction::Type::Log)) {
215 *output = ScriptBreakpointActionTypeLog;
216 return true;
217 }
218 if (typeString == Inspector::Protocol::getEnumConstantValue(Inspector::Protocol::Debugger::BreakpointAction::Type::Evaluate)) {
219 *output = ScriptBreakpointActionTypeEvaluate;
220 return true;
221 }
222 if (typeString == Inspector::Protocol::getEnumConstantValue(Inspector::Protocol::Debugger::BreakpointAction::Type::Sound)) {
223 *output = ScriptBreakpointActionTypeSound;
224 return true;
225 }
226 if (typeString == Inspector::Protocol::getEnumConstantValue(Inspector::Protocol::Debugger::BreakpointAction::Type::Probe)) {
227 *output = ScriptBreakpointActionTypeProbe;
228 return true;
229 }
230
231 return false;
232 }
233
234 bool InspectorDebuggerAgent::breakpointActionsFromProtocol(ErrorString& errorString, RefPtr<InspectorArray>& actions, BreakpointActions* result)
235 {
236 if (!actions)
237 return true;
238
239 unsigned actionsLength = actions->length();
240 if (!actionsLength)
241 return true;
242
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");
249 return false;
250 }
251
252 String typeString;
253 if (!object->getString(ASCIILiteral("type"), typeString)) {
254 errorString = ASCIILiteral("BreakpointAction had type missing");
255 return false;
256 }
257
258 ScriptBreakpointActionType type;
259 if (!breakpointActionTypeForString(typeString, &type)) {
260 errorString = ASCIILiteral("BreakpointAction had unknown type");
261 return false;
262 }
263
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.
266 int identifier = 0;
267 object->getInteger(ASCIILiteral("id"), identifier);
268
269 String data;
270 object->getString(ASCIILiteral("data"), data);
271
272 result->append(ScriptBreakpointAction(type, identifier, data));
273 }
274
275 return true;
276 }
277
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)
279 {
280 locations = Inspector::Protocol::Array<Inspector::Protocol::Debugger::Location>::create();
281 if (!optionalURL == !optionalURLRegex) {
282 errorString = ASCIILiteral("Either url or urlRegex must be specified.");
283 return;
284 }
285
286 String url = optionalURL ? *optionalURL : *optionalURLRegex;
287 int columnNumber = optionalColumnNumber ? *optionalColumnNumber : 0;
288 bool isRegex = optionalURLRegex;
289
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.");
293 return;
294 }
295
296 String condition = emptyString();
297 bool autoContinue = false;
298 RefPtr<InspectorArray> actions;
299 if (options) {
300 options->getString(ASCIILiteral("condition"), condition);
301 options->getBoolean(ASCIILiteral("autoContinue"), autoContinue);
302 options->getArray(ASCIILiteral("actions"), actions);
303 }
304
305 BreakpointActions breakpointActions;
306 if (!breakpointActionsFromProtocol(errorString, actions, &breakpointActions))
307 return;
308
309 m_javaScriptBreakpoints.set(breakpointIdentifier, buildObjectForBreakpointCookie(url, lineNumber, columnNumber, condition, actions, isRegex, autoContinue));
310
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))
315 continue;
316
317 RefPtr<Inspector::Protocol::Debugger::Location> location = resolveBreakpoint(breakpointIdentifier, it->key, breakpoint);
318 if (location)
319 locations->addItem(WTF::move(location));
320 }
321 *outBreakpointIdentifier = breakpointIdentifier;
322 }
323
324 static bool parseLocation(ErrorString& errorString, const InspectorObject& location, JSC::SourceID& sourceID, unsigned& lineNumber, unsigned& columnNumber)
325 {
326 String scriptIDStr;
327 if (!location.getString(ASCIILiteral("scriptId"), scriptIDStr) || !location.getInteger(ASCIILiteral("lineNumber"), lineNumber)) {
328 sourceID = JSC::noSourceID;
329 errorString = ASCIILiteral("scriptId and lineNumber are required.");
330 return false;
331 }
332
333 sourceID = scriptIDStr.toIntPtr();
334 columnNumber = 0;
335 location.getInteger(ASCIILiteral("columnNumber"), columnNumber);
336 return true;
337 }
338
339 void InspectorDebuggerAgent::setBreakpoint(ErrorString& errorString, const InspectorObject& location, const InspectorObject* options, Inspector::Protocol::Debugger::BreakpointId* outBreakpointIdentifier, RefPtr<Inspector::Protocol::Debugger::Location>& actualLocation)
340 {
341 JSC::SourceID sourceID;
342 unsigned lineNumber;
343 unsigned columnNumber;
344 if (!parseLocation(errorString, location, sourceID, lineNumber, columnNumber))
345 return;
346
347 String condition = emptyString();
348 bool autoContinue = false;
349 RefPtr<InspectorArray> actions;
350 if (options) {
351 options->getString(ASCIILiteral("condition"), condition);
352 options->getBoolean(ASCIILiteral("autoContinue"), autoContinue);
353 options->getArray(ASCIILiteral("actions"), actions);
354 }
355
356 BreakpointActions breakpointActions;
357 if (!breakpointActionsFromProtocol(errorString, actions, &breakpointActions))
358 return;
359
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.");
363 return;
364 }
365
366 ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition, breakpointActions, autoContinue);
367 actualLocation = resolveBreakpoint(breakpointIdentifier, sourceID, breakpoint);
368 if (!actualLocation) {
369 errorString = ASCIILiteral("Could not resolve breakpoint");
370 return;
371 }
372
373 *outBreakpointIdentifier = breakpointIdentifier;
374 }
375
376 void InspectorDebuggerAgent::removeBreakpoint(ErrorString&, const String& breakpointIdentifier)
377 {
378 m_javaScriptBreakpoints.remove(breakpointIdentifier);
379
380 for (JSC::BreakpointID breakpointID : m_breakpointIdentifierToDebugServerBreakpointIDs.take(breakpointIdentifier)) {
381 m_debuggerBreakpointIdentifierToInspectorBreakpointIdentifier.remove(breakpointID);
382
383 const BreakpointActions& breakpointActions = scriptDebugServer().getActionsForBreakpoint(breakpointID);
384 for (auto& action : breakpointActions)
385 m_injectedScriptManager->releaseObjectGroup(objectGroupForBreakpointAction(action));
386
387 scriptDebugServer().removeBreakpoint(breakpointID);
388 }
389 }
390
391 void InspectorDebuggerAgent::continueToLocation(ErrorString& errorString, const InspectorObject& location)
392 {
393 if (m_continueToLocationBreakpointID != JSC::noBreakpointID) {
394 scriptDebugServer().removeBreakpoint(m_continueToLocationBreakpointID);
395 m_continueToLocationBreakpointID = JSC::noBreakpointID;
396 }
397
398 JSC::SourceID sourceID;
399 unsigned lineNumber;
400 unsigned columnNumber;
401 if (!parseLocation(errorString, location, sourceID, lineNumber, columnNumber))
402 return;
403
404 ScriptBreakpoint breakpoint(lineNumber, columnNumber, "", false);
405 m_continueToLocationBreakpointID = scriptDebugServer().setBreakpoint(sourceID, breakpoint, &lineNumber, &columnNumber);
406 resume(errorString);
407 }
408
409 RefPtr<Inspector::Protocol::Debugger::Location> InspectorDebuggerAgent::resolveBreakpoint(const String& breakpointIdentifier, JSC::SourceID sourceID, const ScriptBreakpoint& breakpoint)
410 {
411 ScriptsMap::iterator scriptIterator = m_scripts.find(sourceID);
412 if (scriptIterator == m_scripts.end())
413 return nullptr;
414 Script& script = scriptIterator->value;
415 if (breakpoint.lineNumber < script.startLine || script.endLine < breakpoint.lineNumber)
416 return nullptr;
417
418 unsigned actualLineNumber;
419 unsigned actualColumnNumber;
420 JSC::BreakpointID debugServerBreakpointID = scriptDebugServer().setBreakpoint(sourceID, breakpoint, &actualLineNumber, &actualColumnNumber);
421 if (debugServerBreakpointID == JSC::noBreakpointID)
422 return nullptr;
423
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);
428
429 m_debuggerBreakpointIdentifierToInspectorBreakpointIdentifier.set(debugServerBreakpointID, breakpointIdentifier);
430
431 auto location = Inspector::Protocol::Debugger::Location::create()
432 .setScriptId(String::number(sourceID))
433 .setLineNumber(actualLineNumber)
434 .release();
435 location->setColumnNumber(actualColumnNumber);
436 return WTF::move(location);
437 }
438
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)
440 {
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;
445 return;
446 }
447
448 bool isRegex = optionalIsRegex ? *optionalIsRegex : false;
449 bool caseSensitive = optionalCaseSensitive ? *optionalCaseSensitive : false;
450 results = ContentSearchUtilities::searchInTextByLines(it->value.source, query, caseSensitive, isRegex);
451 }
452
453 void InspectorDebuggerAgent::getScriptSource(ErrorString& error, const String& scriptIDStr, String* scriptSource)
454 {
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;
459 else
460 error = ASCIILiteral("No script for id: ") + scriptIDStr;
461 }
462
463 void InspectorDebuggerAgent::getFunctionDetails(ErrorString& errorString, const String& functionId, RefPtr<Inspector::Protocol::Debugger::FunctionDetails>& details)
464 {
465 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(functionId);
466 if (injectedScript.hasNoValue()) {
467 errorString = ASCIILiteral("Function object id is obsolete");
468 return;
469 }
470
471 injectedScript.getFunctionDetails(errorString, functionId, &details);
472 }
473
474 void InspectorDebuggerAgent::schedulePauseOnNextStatement(DebuggerFrontendDispatcher::Reason breakReason, RefPtr<InspectorObject>&& data)
475 {
476 if (m_javaScriptPauseScheduled)
477 return;
478
479 m_breakReason = breakReason;
480 m_breakAuxData = WTF::move(data);
481 scriptDebugServer().setPauseOnNextStatement(true);
482 }
483
484 void InspectorDebuggerAgent::cancelPauseOnNextStatement()
485 {
486 if (m_javaScriptPauseScheduled)
487 return;
488
489 clearBreakDetails();
490 scriptDebugServer().setPauseOnNextStatement(false);
491 }
492
493 void InspectorDebuggerAgent::pause(ErrorString&)
494 {
495 schedulePauseOnNextStatement(DebuggerFrontendDispatcher::Reason::PauseOnNextStatement, nullptr);
496
497 m_javaScriptPauseScheduled = true;
498 }
499
500 void InspectorDebuggerAgent::resume(ErrorString& errorString)
501 {
502 if (!assertPaused(errorString))
503 return;
504
505 scriptDebugServer().continueProgram();
506 }
507
508 void InspectorDebuggerAgent::stepOver(ErrorString& errorString)
509 {
510 if (!assertPaused(errorString))
511 return;
512
513 scriptDebugServer().stepOverStatement();
514 }
515
516 void InspectorDebuggerAgent::stepInto(ErrorString& errorString)
517 {
518 if (!assertPaused(errorString))
519 return;
520
521 scriptDebugServer().stepIntoStatement();
522
523 if (m_listener)
524 m_listener->stepInto();
525 }
526
527 void InspectorDebuggerAgent::stepOut(ErrorString& errorString)
528 {
529 if (!assertPaused(errorString))
530 return;
531
532 scriptDebugServer().stepOutOfFunction();
533 }
534
535 void InspectorDebuggerAgent::setPauseOnExceptions(ErrorString& errorString, const String& stringPauseState)
536 {
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;
544 else {
545 errorString = ASCIILiteral("Unknown pause on exceptions mode: ") + stringPauseState;
546 return;
547 }
548
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");
552 }
553
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)
555 {
556 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(callFrameId);
557 if (injectedScript.hasNoValue()) {
558 errorString = ASCIILiteral("Inspected frame has gone");
559 return;
560 }
561
562 JSC::Debugger::PauseOnExceptionsState previousPauseOnExceptionsState = scriptDebugServer().pauseOnExceptionsState();
563 if (doNotPauseOnExceptionsAndMuteConsole ? *doNotPauseOnExceptionsAndMuteConsole : false) {
564 if (previousPauseOnExceptionsState != JSC::Debugger::DontPauseOnExceptions)
565 scriptDebugServer().setPauseOnExceptionsState(JSC::Debugger::DontPauseOnExceptions);
566 muteConsole();
567 }
568
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);
570
571 if (doNotPauseOnExceptionsAndMuteConsole ? *doNotPauseOnExceptionsAndMuteConsole : false) {
572 unmuteConsole();
573 if (scriptDebugServer().pauseOnExceptionsState() != previousPauseOnExceptionsState)
574 scriptDebugServer().setPauseOnExceptionsState(previousPauseOnExceptionsState);
575 }
576 }
577
578 void InspectorDebuggerAgent::setOverlayMessage(ErrorString&, const String*)
579 {
580 }
581
582 void InspectorDebuggerAgent::scriptExecutionBlockedByCSP(const String& directiveText)
583 {
584 if (scriptDebugServer().pauseOnExceptionsState() != JSC::Debugger::DontPauseOnExceptions)
585 breakProgram(DebuggerFrontendDispatcher::Reason::CSPViolation, buildCSPViolationPauseReason(directiveText));
586 }
587
588 Ref<Inspector::Protocol::Array<Inspector::Protocol::Debugger::CallFrame>> InspectorDebuggerAgent::currentCallFrames(InjectedScript injectedScript)
589 {
590 ASSERT(!injectedScript.hasNoValue());
591 if (injectedScript.hasNoValue())
592 return Inspector::Protocol::Array<Inspector::Protocol::Debugger::CallFrame>::create();
593
594 return injectedScript.wrapCallFrames(m_currentCallStack);
595 }
596
597 String InspectorDebuggerAgent::sourceMapURLForScript(const Script& script)
598 {
599 return ContentSearchUtilities::findScriptSourceMapURL(script.source);
600 }
601
602 void InspectorDebuggerAgent::didParseSource(JSC::SourceID sourceID, const Script& inScript)
603 {
604 Script script = inScript;
605 if (script.startLine <= 0 && !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;
624 if (!it->value->asObject(breakpointObject))
625 return;
626
627 bool isRegex;
628 breakpointObject->getBoolean(ASCIILiteral("isRegex"), isRegex);
629 String url;
630 breakpointObject->getString(ASCIILiteral("url"), url);
631 if (!matches(scriptURL, url, isRegex))
632 continue;
633
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();
644 continue;
645 }
646
647 RefPtr<Inspector::Protocol::Debugger::Location> location = resolveBreakpoint(it->key, sourceID, breakpoint);
648 if (location)
649 m_frontendDispatcher->breakpointResolved(it->key, location);
650 }
651 }
652
653 void InspectorDebuggerAgent::failedToParseSource(const String& url, const String& data, int firstLine, int errorLine, const String& errorMessage)
654 {
655 m_frontendDispatcher->scriptFailedToParse(url, data, firstLine, errorLine, errorMessage);
656 }
657
658 void InspectorDebuggerAgent::didPause(JSC::ExecState* scriptState, const Deprecated::ScriptValue& callFrames, const Deprecated::ScriptValue& exceptionOrCaughtValue)
659 {
660 ASSERT(scriptState && !m_pausedScriptState);
661 m_pausedScriptState = scriptState;
662 m_currentCallStack = callFrames;
663
664 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(scriptState);
665
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);
674 }
675 break;
676 }
677 case JSC::Debugger::PausedForDebuggerStatement:
678 m_breakReason = DebuggerFrontendDispatcher::Reason::DebuggerStatement;
679 m_breakAuxData = nullptr;
680 break;
681 case JSC::Debugger::PausedForException:
682 m_breakReason = DebuggerFrontendDispatcher::Reason::Exception;
683 m_breakAuxData = buildExceptionPauseReason(exceptionOrCaughtValue, injectedScript);
684 break;
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.
691 break;
692 case JSC::Debugger::NotPaused:
693 ASSERT_NOT_REACHED();
694 break;
695 }
696 }
697
698 // Set $exception to the exception or caught value.
699 if (!exceptionOrCaughtValue.hasNoValue() && !injectedScript.hasNoValue()) {
700 injectedScript.setExceptionValue(exceptionOrCaughtValue);
701 m_hasExceptionValue = true;
702 }
703
704 m_frontendDispatcher->paused(currentCallFrames(injectedScript), m_breakReason, m_breakAuxData);
705 m_javaScriptPauseScheduled = false;
706
707 if (m_continueToLocationBreakpointID != JSC::noBreakpointID) {
708 scriptDebugServer().removeBreakpoint(m_continueToLocationBreakpointID);
709 m_continueToLocationBreakpointID = JSC::noBreakpointID;
710 }
711
712 if (m_listener)
713 m_listener->didPause();
714
715 RefPtr<Stopwatch> stopwatch = m_injectedScriptManager->inspectorEnvironment().executionStopwatch();
716 if (stopwatch && stopwatch->isActive()) {
717 stopwatch->stop();
718 m_didPauseStopwatch = true;
719 }
720 }
721
722 void InspectorDebuggerAgent::breakpointActionSound(int breakpointActionIdentifier)
723 {
724 m_frontendDispatcher->playBreakpointActionSound(breakpointActionIdentifier);
725 }
726
727 void InspectorDebuggerAgent::breakpointActionProbe(JSC::ExecState* scriptState, const ScriptBreakpointAction& action, unsigned batchId, unsigned sampleId, const Deprecated::ScriptValue& sample)
728 {
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)
733 .setBatchId(batchId)
734 .setSampleId(sampleId)
735 .setTimestamp(m_injectedScriptManager->inspectorEnvironment().executionStopwatch()->elapsedTime())
736 .setPayload(payload.release())
737 .release();
738
739 m_frontendDispatcher->didSampleProbe(WTF::move(result));
740 }
741
742 void InspectorDebuggerAgent::didContinue()
743 {
744 if (m_didPauseStopwatch) {
745 m_didPauseStopwatch = false;
746 m_injectedScriptManager->inspectorEnvironment().executionStopwatch()->start();
747 }
748
749 m_pausedScriptState = nullptr;
750 m_currentCallStack = Deprecated::ScriptValue();
751 m_injectedScriptManager->releaseObjectGroup(InspectorDebuggerAgent::backtraceObjectGroup);
752 clearBreakDetails();
753 clearExceptionValue();
754
755 m_frontendDispatcher->resumed();
756 }
757
758 void InspectorDebuggerAgent::breakProgram(DebuggerFrontendDispatcher::Reason breakReason, RefPtr<InspectorObject>&& data)
759 {
760 m_breakReason = breakReason;
761 m_breakAuxData = WTF::move(data);
762 scriptDebugServer().breakProgram();
763 }
764
765 void InspectorDebuggerAgent::clearInspectorBreakpointState()
766 {
767 ErrorString dummyError;
768 Vector<String> breakpointIdentifiers;
769 copyKeysToVector(m_breakpointIdentifierToDebugServerBreakpointIDs, breakpointIdentifiers);
770 for (const String& identifier : breakpointIdentifiers)
771 removeBreakpoint(dummyError, identifier);
772
773 m_javaScriptBreakpoints.clear();
774
775 clearDebuggerBreakpointState();
776 }
777
778 void InspectorDebuggerAgent::clearDebuggerBreakpointState()
779 {
780 scriptDebugServer().clearBreakpoints();
781
782 m_pausedScriptState = nullptr;
783 m_currentCallStack = Deprecated::ScriptValue();
784 m_scripts.clear();
785 m_breakpointIdentifierToDebugServerBreakpointIDs.clear();
786 m_debuggerBreakpointIdentifierToInspectorBreakpointIdentifier.clear();
787 m_continueToLocationBreakpointID = JSC::noBreakpointID;
788 clearBreakDetails();
789 m_javaScriptPauseScheduled = false;
790 m_hasExceptionValue = false;
791
792 scriptDebugServer().continueProgram();
793 }
794
795 void InspectorDebuggerAgent::didClearGlobalObject()
796 {
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();
800
801 if (m_frontendDispatcher)
802 m_frontendDispatcher->globalObjectCleared();
803 }
804
805 bool InspectorDebuggerAgent::assertPaused(ErrorString& errorString)
806 {
807 if (!m_pausedScriptState) {
808 errorString = ASCIILiteral("Can only perform operation while paused.");
809 return false;
810 }
811
812 return true;
813 }
814
815 void InspectorDebuggerAgent::clearBreakDetails()
816 {
817 m_breakReason = DebuggerFrontendDispatcher::Reason::Other;
818 m_breakAuxData = nullptr;
819 }
820
821 void InspectorDebuggerAgent::clearExceptionValue()
822 {
823 if (m_hasExceptionValue) {
824 m_injectedScriptManager->clearExceptionValue();
825 m_hasExceptionValue = false;
826 }
827 }
828
829 } // namespace Inspector