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