blob: 80e261119e8dad855314b4df6de7381974fac01f [file] [log] [blame]
Ben Murdochf3b273f2017-01-17 12:11:28 +00001// Copyright 2015 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/inspector/v8-debugger-agent-impl.h"
6
7#include <algorithm>
8
9#include "src/inspector/injected-script.h"
10#include "src/inspector/inspected-context.h"
11#include "src/inspector/java-script-call-frame.h"
12#include "src/inspector/protocol/Protocol.h"
13#include "src/inspector/remote-object-id.h"
14#include "src/inspector/script-breakpoint.h"
15#include "src/inspector/search-util.h"
16#include "src/inspector/string-util.h"
17#include "src/inspector/v8-debugger-script.h"
18#include "src/inspector/v8-debugger.h"
19#include "src/inspector/v8-inspector-impl.h"
20#include "src/inspector/v8-inspector-session-impl.h"
21#include "src/inspector/v8-regex.h"
22#include "src/inspector/v8-runtime-agent-impl.h"
23#include "src/inspector/v8-stack-trace-impl.h"
24
25#include "include/v8-inspector.h"
26
27namespace v8_inspector {
28
29using protocol::Array;
30using protocol::Maybe;
31using protocol::Debugger::BreakpointId;
32using protocol::Debugger::CallFrame;
33using protocol::Runtime::ExceptionDetails;
34using protocol::Runtime::ScriptId;
35using protocol::Runtime::StackTrace;
36using protocol::Runtime::RemoteObject;
37
38namespace DebuggerAgentState {
39static const char javaScriptBreakpoints[] = "javaScriptBreakopints";
40static const char pauseOnExceptionsState[] = "pauseOnExceptionsState";
41static const char asyncCallStackDepth[] = "asyncCallStackDepth";
42static const char blackboxPattern[] = "blackboxPattern";
43static const char debuggerEnabled[] = "debuggerEnabled";
44
45// Breakpoint properties.
46static const char url[] = "url";
47static const char isRegex[] = "isRegex";
48static const char lineNumber[] = "lineNumber";
49static const char columnNumber[] = "columnNumber";
50static const char condition[] = "condition";
51static const char skipAllPauses[] = "skipAllPauses";
52
53} // namespace DebuggerAgentState
54
55static const int maxSkipStepFrameCount = 128;
56static const char backtraceObjectGroup[] = "backtrace";
57
58static String16 breakpointIdSuffix(
59 V8DebuggerAgentImpl::BreakpointSource source) {
60 switch (source) {
61 case V8DebuggerAgentImpl::UserBreakpointSource:
62 break;
63 case V8DebuggerAgentImpl::DebugCommandBreakpointSource:
64 return ":debug";
65 case V8DebuggerAgentImpl::MonitorCommandBreakpointSource:
66 return ":monitor";
67 }
68 return String16();
69}
70
71static String16 generateBreakpointId(
72 const String16& scriptId, int lineNumber, int columnNumber,
73 V8DebuggerAgentImpl::BreakpointSource source) {
74 return scriptId + ":" + String16::fromInteger(lineNumber) + ":" +
75 String16::fromInteger(columnNumber) + breakpointIdSuffix(source);
76}
77
78static bool positionComparator(const std::pair<int, int>& a,
79 const std::pair<int, int>& b) {
80 if (a.first != b.first) return a.first < b.first;
81 return a.second < b.second;
82}
83
84static bool hasInternalError(ErrorString* errorString, bool hasError) {
85 if (hasError) *errorString = "Internal error";
86 return hasError;
87}
88
89static std::unique_ptr<protocol::Debugger::Location> buildProtocolLocation(
90 const String16& scriptId, int lineNumber, int columnNumber) {
91 return protocol::Debugger::Location::create()
92 .setScriptId(scriptId)
93 .setLineNumber(lineNumber)
94 .setColumnNumber(columnNumber)
95 .build();
96}
97
98V8DebuggerAgentImpl::V8DebuggerAgentImpl(
99 V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
100 protocol::DictionaryValue* state)
101 : m_inspector(session->inspector()),
102 m_debugger(m_inspector->debugger()),
103 m_session(session),
104 m_enabled(false),
105 m_state(state),
106 m_frontend(frontendChannel),
107 m_isolate(m_inspector->isolate()),
108 m_breakReason(protocol::Debugger::Paused::ReasonEnum::Other),
109 m_scheduledDebuggerStep(NoStep),
110 m_skipNextDebuggerStepOut(false),
111 m_javaScriptPauseScheduled(false),
112 m_steppingFromFramework(false),
113 m_pausingOnNativeEvent(false),
114 m_skippedStepFrameCount(0),
115 m_recursionLevelForStepOut(0),
116 m_recursionLevelForStepFrame(0),
117 m_skipAllPauses(false) {
118 clearBreakDetails();
119}
120
121V8DebuggerAgentImpl::~V8DebuggerAgentImpl() {}
122
123bool V8DebuggerAgentImpl::checkEnabled(ErrorString* errorString) {
124 if (enabled()) return true;
125 *errorString = "Debugger agent is not enabled";
126 return false;
127}
128
129void V8DebuggerAgentImpl::enable() {
130 // m_inspector->addListener may result in reporting all parsed scripts to
131 // the agent so it should already be in enabled state by then.
132 m_enabled = true;
133 m_state->setBoolean(DebuggerAgentState::debuggerEnabled, true);
134 m_debugger->enable();
135
136 std::vector<std::unique_ptr<V8DebuggerScript>> compiledScripts;
137 m_debugger->getCompiledScripts(m_session->contextGroupId(), compiledScripts);
138 for (size_t i = 0; i < compiledScripts.size(); i++)
139 didParseSource(std::move(compiledScripts[i]), true);
140
141 // FIXME(WK44513): breakpoints activated flag should be synchronized between
142 // all front-ends
143 m_debugger->setBreakpointsActivated(true);
144}
145
146bool V8DebuggerAgentImpl::enabled() { return m_enabled; }
147
148void V8DebuggerAgentImpl::enable(ErrorString* errorString) {
149 if (enabled()) return;
150
151 if (!m_inspector->client()->canExecuteScripts(m_session->contextGroupId())) {
152 *errorString = "Script execution is prohibited";
153 return;
154 }
155
156 enable();
157}
158
159void V8DebuggerAgentImpl::disable(ErrorString*) {
160 if (!enabled()) return;
161
162 m_state->setObject(DebuggerAgentState::javaScriptBreakpoints,
163 protocol::DictionaryValue::create());
164 m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState,
165 V8Debugger::DontPauseOnExceptions);
166 m_state->setInteger(DebuggerAgentState::asyncCallStackDepth, 0);
167
168 if (!m_pausedContext.IsEmpty()) m_debugger->continueProgram();
169 m_debugger->disable();
170 m_pausedContext.Reset();
171 JavaScriptCallFrames emptyCallFrames;
172 m_pausedCallFrames.swap(emptyCallFrames);
173 m_scripts.clear();
174 m_blackboxedPositions.clear();
175 m_breakpointIdToDebuggerBreakpointIds.clear();
176 m_debugger->setAsyncCallStackDepth(this, 0);
177 m_continueToLocationBreakpointId = String16();
178 clearBreakDetails();
179 m_scheduledDebuggerStep = NoStep;
180 m_skipNextDebuggerStepOut = false;
181 m_javaScriptPauseScheduled = false;
182 m_steppingFromFramework = false;
183 m_pausingOnNativeEvent = false;
184 m_skippedStepFrameCount = 0;
185 m_recursionLevelForStepFrame = 0;
186 m_skipAllPauses = false;
187 m_blackboxPattern = nullptr;
188 m_state->remove(DebuggerAgentState::blackboxPattern);
189 m_enabled = false;
190 m_state->setBoolean(DebuggerAgentState::debuggerEnabled, false);
191}
192
193void V8DebuggerAgentImpl::restore() {
194 DCHECK(!m_enabled);
195 if (!m_state->booleanProperty(DebuggerAgentState::debuggerEnabled, false))
196 return;
197 if (!m_inspector->client()->canExecuteScripts(m_session->contextGroupId()))
198 return;
199
200 enable();
201 ErrorString error;
202
203 int pauseState = V8Debugger::DontPauseOnExceptions;
204 m_state->getInteger(DebuggerAgentState::pauseOnExceptionsState, &pauseState);
205 setPauseOnExceptionsImpl(&error, pauseState);
206 DCHECK(error.isEmpty());
207
208 m_skipAllPauses =
209 m_state->booleanProperty(DebuggerAgentState::skipAllPauses, false);
210
211 int asyncCallStackDepth = 0;
212 m_state->getInteger(DebuggerAgentState::asyncCallStackDepth,
213 &asyncCallStackDepth);
214 m_debugger->setAsyncCallStackDepth(this, asyncCallStackDepth);
215
216 String16 blackboxPattern;
217 if (m_state->getString(DebuggerAgentState::blackboxPattern,
218 &blackboxPattern)) {
219 if (!setBlackboxPattern(&error, blackboxPattern)) UNREACHABLE();
220 }
221}
222
223void V8DebuggerAgentImpl::setBreakpointsActive(ErrorString* errorString,
224 bool active) {
225 if (!checkEnabled(errorString)) return;
226 m_debugger->setBreakpointsActivated(active);
227}
228
229void V8DebuggerAgentImpl::setSkipAllPauses(ErrorString*, bool skip) {
230 m_skipAllPauses = skip;
231 m_state->setBoolean(DebuggerAgentState::skipAllPauses, m_skipAllPauses);
232}
233
234static std::unique_ptr<protocol::DictionaryValue>
235buildObjectForBreakpointCookie(const String16& url, int lineNumber,
236 int columnNumber, const String16& condition,
237 bool isRegex) {
238 std::unique_ptr<protocol::DictionaryValue> breakpointObject =
239 protocol::DictionaryValue::create();
240 breakpointObject->setString(DebuggerAgentState::url, url);
241 breakpointObject->setInteger(DebuggerAgentState::lineNumber, lineNumber);
242 breakpointObject->setInteger(DebuggerAgentState::columnNumber, columnNumber);
243 breakpointObject->setString(DebuggerAgentState::condition, condition);
244 breakpointObject->setBoolean(DebuggerAgentState::isRegex, isRegex);
245 return breakpointObject;
246}
247
248static bool matches(V8InspectorImpl* inspector, const String16& url,
249 const String16& pattern, bool isRegex) {
250 if (isRegex) {
251 V8Regex regex(inspector, pattern, true);
252 return regex.match(url) != -1;
253 }
254 return url == pattern;
255}
256
257void V8DebuggerAgentImpl::setBreakpointByUrl(
258 ErrorString* errorString, int lineNumber,
259 const Maybe<String16>& optionalURL, const Maybe<String16>& optionalURLRegex,
260 const Maybe<int>& optionalColumnNumber,
261 const Maybe<String16>& optionalCondition, String16* outBreakpointId,
262 std::unique_ptr<protocol::Array<protocol::Debugger::Location>>* locations) {
263 *locations = Array<protocol::Debugger::Location>::create();
264 if (optionalURL.isJust() == optionalURLRegex.isJust()) {
265 *errorString = "Either url or urlRegex must be specified.";
266 return;
267 }
268
269 String16 url = optionalURL.isJust() ? optionalURL.fromJust()
270 : optionalURLRegex.fromJust();
271 int columnNumber = 0;
272 if (optionalColumnNumber.isJust()) {
273 columnNumber = optionalColumnNumber.fromJust();
274 if (columnNumber < 0) {
275 *errorString = "Incorrect column number";
276 return;
277 }
278 }
279 String16 condition = optionalCondition.fromMaybe("");
280 bool isRegex = optionalURLRegex.isJust();
281
282 String16 breakpointId = (isRegex ? "/" + url + "/" : url) + ":" +
283 String16::fromInteger(lineNumber) + ":" +
284 String16::fromInteger(columnNumber);
285 protocol::DictionaryValue* breakpointsCookie =
286 m_state->getObject(DebuggerAgentState::javaScriptBreakpoints);
287 if (!breakpointsCookie) {
288 std::unique_ptr<protocol::DictionaryValue> newValue =
289 protocol::DictionaryValue::create();
290 breakpointsCookie = newValue.get();
291 m_state->setObject(DebuggerAgentState::javaScriptBreakpoints,
292 std::move(newValue));
293 }
294 if (breakpointsCookie->get(breakpointId)) {
295 *errorString = "Breakpoint at specified location already exists.";
296 return;
297 }
298
299 breakpointsCookie->setObject(
300 breakpointId, buildObjectForBreakpointCookie(
301 url, lineNumber, columnNumber, condition, isRegex));
302
303 ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition);
304 for (const auto& script : m_scripts) {
305 if (!matches(m_inspector, script.second->sourceURL(), url, isRegex))
306 continue;
307 std::unique_ptr<protocol::Debugger::Location> location = resolveBreakpoint(
308 breakpointId, script.first, breakpoint, UserBreakpointSource);
309 if (location) (*locations)->addItem(std::move(location));
310 }
311
312 *outBreakpointId = breakpointId;
313}
314
315static bool parseLocation(
316 ErrorString* errorString,
317 std::unique_ptr<protocol::Debugger::Location> location, String16* scriptId,
318 int* lineNumber, int* columnNumber) {
319 *scriptId = location->getScriptId();
320 *lineNumber = location->getLineNumber();
321 *columnNumber = location->getColumnNumber(0);
322 return true;
323}
324
325void V8DebuggerAgentImpl::setBreakpoint(
326 ErrorString* errorString,
327 std::unique_ptr<protocol::Debugger::Location> location,
328 const Maybe<String16>& optionalCondition, String16* outBreakpointId,
329 std::unique_ptr<protocol::Debugger::Location>* actualLocation) {
330 String16 scriptId;
331 int lineNumber;
332 int columnNumber;
333
334 if (!parseLocation(errorString, std::move(location), &scriptId, &lineNumber,
335 &columnNumber))
336 return;
337
338 String16 condition = optionalCondition.fromMaybe("");
339
340 String16 breakpointId = generateBreakpointId(
341 scriptId, lineNumber, columnNumber, UserBreakpointSource);
342 if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
343 m_breakpointIdToDebuggerBreakpointIds.end()) {
344 *errorString = "Breakpoint at specified location already exists.";
345 return;
346 }
347 ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition);
348 *actualLocation = resolveBreakpoint(breakpointId, scriptId, breakpoint,
349 UserBreakpointSource);
350 if (*actualLocation)
351 *outBreakpointId = breakpointId;
352 else
353 *errorString = "Could not resolve breakpoint";
354}
355
356void V8DebuggerAgentImpl::removeBreakpoint(ErrorString* errorString,
357 const String16& breakpointId) {
358 if (!checkEnabled(errorString)) return;
359 protocol::DictionaryValue* breakpointsCookie =
360 m_state->getObject(DebuggerAgentState::javaScriptBreakpoints);
361 if (breakpointsCookie) breakpointsCookie->remove(breakpointId);
362 removeBreakpoint(breakpointId);
363}
364
365void V8DebuggerAgentImpl::removeBreakpoint(const String16& breakpointId) {
366 DCHECK(enabled());
367 BreakpointIdToDebuggerBreakpointIdsMap::iterator
368 debuggerBreakpointIdsIterator =
369 m_breakpointIdToDebuggerBreakpointIds.find(breakpointId);
370 if (debuggerBreakpointIdsIterator ==
371 m_breakpointIdToDebuggerBreakpointIds.end())
372 return;
373 const std::vector<String16>& ids = debuggerBreakpointIdsIterator->second;
374 for (size_t i = 0; i < ids.size(); ++i) {
375 const String16& debuggerBreakpointId = ids[i];
376
377 m_debugger->removeBreakpoint(debuggerBreakpointId);
378 m_serverBreakpoints.erase(debuggerBreakpointId);
379 }
380 m_breakpointIdToDebuggerBreakpointIds.erase(breakpointId);
381}
382
383void V8DebuggerAgentImpl::continueToLocation(
384 ErrorString* errorString,
385 std::unique_ptr<protocol::Debugger::Location> location) {
386 if (!checkEnabled(errorString)) return;
387 if (!m_continueToLocationBreakpointId.isEmpty()) {
388 m_debugger->removeBreakpoint(m_continueToLocationBreakpointId);
389 m_continueToLocationBreakpointId = "";
390 }
391
392 String16 scriptId;
393 int lineNumber;
394 int columnNumber;
395
396 if (!parseLocation(errorString, std::move(location), &scriptId, &lineNumber,
397 &columnNumber))
398 return;
399
400 ScriptBreakpoint breakpoint(lineNumber, columnNumber, "");
401 m_continueToLocationBreakpointId = m_debugger->setBreakpoint(
402 scriptId, breakpoint, &lineNumber, &columnNumber);
403 resume(errorString);
404}
405
406bool V8DebuggerAgentImpl::isCurrentCallStackEmptyOrBlackboxed() {
407 DCHECK(enabled());
408 JavaScriptCallFrames callFrames = m_debugger->currentCallFrames();
409 for (size_t index = 0; index < callFrames.size(); ++index) {
410 if (!isCallFrameWithUnknownScriptOrBlackboxed(callFrames[index].get()))
411 return false;
412 }
413 return true;
414}
415
416bool V8DebuggerAgentImpl::isTopPausedCallFrameBlackboxed() {
417 DCHECK(enabled());
418 JavaScriptCallFrame* frame =
419 m_pausedCallFrames.size() ? m_pausedCallFrames[0].get() : nullptr;
420 return isCallFrameWithUnknownScriptOrBlackboxed(frame);
421}
422
423bool V8DebuggerAgentImpl::isCallFrameWithUnknownScriptOrBlackboxed(
424 JavaScriptCallFrame* frame) {
425 if (!frame) return true;
426 ScriptsMap::iterator it =
427 m_scripts.find(String16::fromInteger(frame->sourceID()));
428 if (it == m_scripts.end()) {
429 // Unknown scripts are blackboxed.
430 return true;
431 }
432 if (m_blackboxPattern) {
433 const String16& scriptSourceURL = it->second->sourceURL();
434 if (!scriptSourceURL.isEmpty() &&
435 m_blackboxPattern->match(scriptSourceURL) != -1)
436 return true;
437 }
438 auto itBlackboxedPositions =
439 m_blackboxedPositions.find(String16::fromInteger(frame->sourceID()));
440 if (itBlackboxedPositions == m_blackboxedPositions.end()) return false;
441
442 const std::vector<std::pair<int, int>>& ranges =
443 itBlackboxedPositions->second;
444 auto itRange = std::lower_bound(
445 ranges.begin(), ranges.end(),
446 std::make_pair(frame->line(), frame->column()), positionComparator);
447 // Ranges array contains positions in script where blackbox state is changed.
448 // [(0,0) ... ranges[0]) isn't blackboxed, [ranges[0] ... ranges[1]) is
449 // blackboxed...
450 return std::distance(ranges.begin(), itRange) % 2;
451}
452
453V8DebuggerAgentImpl::SkipPauseRequest
454V8DebuggerAgentImpl::shouldSkipExceptionPause(
455 JavaScriptCallFrame* topCallFrame) {
456 if (m_steppingFromFramework) return RequestNoSkip;
457 if (isCallFrameWithUnknownScriptOrBlackboxed(topCallFrame))
458 return RequestContinue;
459 return RequestNoSkip;
460}
461
462V8DebuggerAgentImpl::SkipPauseRequest V8DebuggerAgentImpl::shouldSkipStepPause(
463 JavaScriptCallFrame* topCallFrame) {
464 if (m_steppingFromFramework) return RequestNoSkip;
465
466 if (m_skipNextDebuggerStepOut) {
467 m_skipNextDebuggerStepOut = false;
468 if (m_scheduledDebuggerStep == StepOut) return RequestStepOut;
469 }
470
471 if (!isCallFrameWithUnknownScriptOrBlackboxed(topCallFrame))
472 return RequestNoSkip;
473
474 if (m_skippedStepFrameCount >= maxSkipStepFrameCount) return RequestStepOut;
475
476 if (!m_skippedStepFrameCount) m_recursionLevelForStepFrame = 1;
477
478 ++m_skippedStepFrameCount;
479 return RequestStepFrame;
480}
481
482std::unique_ptr<protocol::Debugger::Location>
483V8DebuggerAgentImpl::resolveBreakpoint(const String16& breakpointId,
484 const String16& scriptId,
485 const ScriptBreakpoint& breakpoint,
486 BreakpointSource source) {
487 DCHECK(enabled());
488 // FIXME: remove these checks once crbug.com/520702 is resolved.
489 CHECK(!breakpointId.isEmpty());
490 CHECK(!scriptId.isEmpty());
491 ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId);
492 if (scriptIterator == m_scripts.end()) return nullptr;
493 if (breakpoint.lineNumber < scriptIterator->second->startLine() ||
494 scriptIterator->second->endLine() < breakpoint.lineNumber)
495 return nullptr;
496
497 int actualLineNumber;
498 int actualColumnNumber;
499 String16 debuggerBreakpointId = m_debugger->setBreakpoint(
500 scriptId, breakpoint, &actualLineNumber, &actualColumnNumber);
501 if (debuggerBreakpointId.isEmpty()) return nullptr;
502
503 m_serverBreakpoints[debuggerBreakpointId] =
504 std::make_pair(breakpointId, source);
505 CHECK(!breakpointId.isEmpty());
506
507 m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
508 debuggerBreakpointId);
509 return buildProtocolLocation(scriptId, actualLineNumber, actualColumnNumber);
510}
511
512void V8DebuggerAgentImpl::searchInContent(
513 ErrorString* error, const String16& scriptId, const String16& query,
514 const Maybe<bool>& optionalCaseSensitive,
515 const Maybe<bool>& optionalIsRegex,
516 std::unique_ptr<Array<protocol::Debugger::SearchMatch>>* results) {
517 v8::HandleScope handles(m_isolate);
518 ScriptsMap::iterator it = m_scripts.find(scriptId);
519 if (it == m_scripts.end()) {
520 *error = String16("No script for id: " + scriptId);
521 return;
522 }
523
524 std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>> matches =
525 searchInTextByLinesImpl(m_session,
526 toProtocolString(it->second->source(m_isolate)),
527 query, optionalCaseSensitive.fromMaybe(false),
528 optionalIsRegex.fromMaybe(false));
529 *results = protocol::Array<protocol::Debugger::SearchMatch>::create();
530 for (size_t i = 0; i < matches.size(); ++i)
531 (*results)->addItem(std::move(matches[i]));
532}
533
534void V8DebuggerAgentImpl::setScriptSource(
535 ErrorString* errorString, const String16& scriptId,
536 const String16& newContent, const Maybe<bool>& dryRun,
537 Maybe<protocol::Array<protocol::Debugger::CallFrame>>* newCallFrames,
538 Maybe<bool>* stackChanged, Maybe<StackTrace>* asyncStackTrace,
539 Maybe<protocol::Runtime::ExceptionDetails>* optOutCompileError) {
540 if (!checkEnabled(errorString)) return;
541
542 v8::HandleScope handles(m_isolate);
543 v8::Local<v8::String> newSource = toV8String(m_isolate, newContent);
544 if (!m_debugger->setScriptSource(scriptId, newSource, dryRun.fromMaybe(false),
545 errorString, optOutCompileError,
546 &m_pausedCallFrames, stackChanged))
547 return;
548
549 ScriptsMap::iterator it = m_scripts.find(scriptId);
550 if (it != m_scripts.end()) it->second->setSource(m_isolate, newSource);
551
552 std::unique_ptr<Array<CallFrame>> callFrames = currentCallFrames(errorString);
553 if (!callFrames) return;
554 *newCallFrames = std::move(callFrames);
555 *asyncStackTrace = currentAsyncStackTrace();
556}
557
558void V8DebuggerAgentImpl::restartFrame(
559 ErrorString* errorString, const String16& callFrameId,
560 std::unique_ptr<Array<CallFrame>>* newCallFrames,
561 Maybe<StackTrace>* asyncStackTrace) {
562 if (!assertPaused(errorString)) return;
563 InjectedScript::CallFrameScope scope(
564 errorString, m_inspector, m_session->contextGroupId(), callFrameId);
565 if (!scope.initialize()) return;
566 if (scope.frameOrdinal() >= m_pausedCallFrames.size()) {
567 *errorString = "Could not find call frame with given id";
568 return;
569 }
570
571 v8::Local<v8::Value> resultValue;
572 v8::Local<v8::Boolean> result;
573 if (!m_pausedCallFrames[scope.frameOrdinal()]->restart().ToLocal(
574 &resultValue) ||
575 scope.tryCatch().HasCaught() ||
576 !resultValue->ToBoolean(scope.context()).ToLocal(&result) ||
577 !result->Value()) {
578 *errorString = "Internal error";
579 return;
580 }
581 JavaScriptCallFrames frames = m_debugger->currentCallFrames();
582 m_pausedCallFrames.swap(frames);
583
584 *newCallFrames = currentCallFrames(errorString);
585 if (!*newCallFrames) return;
586 *asyncStackTrace = currentAsyncStackTrace();
587}
588
589void V8DebuggerAgentImpl::getScriptSource(ErrorString* error,
590 const String16& scriptId,
591 String16* scriptSource) {
592 if (!checkEnabled(error)) return;
593 ScriptsMap::iterator it = m_scripts.find(scriptId);
594 if (it == m_scripts.end()) {
595 *error = "No script for id: " + scriptId;
596 return;
597 }
598 v8::HandleScope handles(m_isolate);
599 *scriptSource = toProtocolString(it->second->source(m_isolate));
600}
601
602void V8DebuggerAgentImpl::schedulePauseOnNextStatement(
603 const String16& breakReason,
604 std::unique_ptr<protocol::DictionaryValue> data) {
605 if (!enabled() || m_scheduledDebuggerStep == StepInto ||
606 m_javaScriptPauseScheduled || m_debugger->isPaused() ||
607 !m_debugger->breakpointsActivated())
608 return;
609 m_breakReason = breakReason;
610 m_breakAuxData = std::move(data);
611 m_pausingOnNativeEvent = true;
612 m_skipNextDebuggerStepOut = false;
613 m_debugger->setPauseOnNextStatement(true);
614}
615
616void V8DebuggerAgentImpl::schedulePauseOnNextStatementIfSteppingInto() {
617 DCHECK(enabled());
618 if (m_scheduledDebuggerStep != StepInto || m_javaScriptPauseScheduled ||
619 m_debugger->isPaused())
620 return;
621 clearBreakDetails();
622 m_pausingOnNativeEvent = false;
623 m_skippedStepFrameCount = 0;
624 m_recursionLevelForStepFrame = 0;
625 m_debugger->setPauseOnNextStatement(true);
626}
627
628void V8DebuggerAgentImpl::cancelPauseOnNextStatement() {
629 if (m_javaScriptPauseScheduled || m_debugger->isPaused()) return;
630 clearBreakDetails();
631 m_pausingOnNativeEvent = false;
632 m_debugger->setPauseOnNextStatement(false);
633}
634
635void V8DebuggerAgentImpl::pause(ErrorString* errorString) {
636 if (!checkEnabled(errorString)) return;
637 if (m_javaScriptPauseScheduled || m_debugger->isPaused()) return;
638 clearBreakDetails();
639 m_javaScriptPauseScheduled = true;
640 m_scheduledDebuggerStep = NoStep;
641 m_skippedStepFrameCount = 0;
642 m_steppingFromFramework = false;
643 m_debugger->setPauseOnNextStatement(true);
644}
645
646void V8DebuggerAgentImpl::resume(ErrorString* errorString) {
647 if (!assertPaused(errorString)) return;
648 m_scheduledDebuggerStep = NoStep;
649 m_steppingFromFramework = false;
650 m_session->releaseObjectGroup(backtraceObjectGroup);
651 m_debugger->continueProgram();
652}
653
654void V8DebuggerAgentImpl::stepOver(ErrorString* errorString) {
655 if (!assertPaused(errorString)) return;
656 // StepOver at function return point should fallback to StepInto.
657 JavaScriptCallFrame* frame =
658 !m_pausedCallFrames.empty() ? m_pausedCallFrames[0].get() : nullptr;
659 if (frame && frame->isAtReturn()) {
660 stepInto(errorString);
661 return;
662 }
663 m_scheduledDebuggerStep = StepOver;
664 m_steppingFromFramework = isTopPausedCallFrameBlackboxed();
665 m_session->releaseObjectGroup(backtraceObjectGroup);
666 m_debugger->stepOverStatement();
667}
668
669void V8DebuggerAgentImpl::stepInto(ErrorString* errorString) {
670 if (!assertPaused(errorString)) return;
671 m_scheduledDebuggerStep = StepInto;
672 m_steppingFromFramework = isTopPausedCallFrameBlackboxed();
673 m_session->releaseObjectGroup(backtraceObjectGroup);
674 m_debugger->stepIntoStatement();
675}
676
677void V8DebuggerAgentImpl::stepOut(ErrorString* errorString) {
678 if (!assertPaused(errorString)) return;
679 m_scheduledDebuggerStep = StepOut;
680 m_skipNextDebuggerStepOut = false;
681 m_recursionLevelForStepOut = 1;
682 m_steppingFromFramework = isTopPausedCallFrameBlackboxed();
683 m_session->releaseObjectGroup(backtraceObjectGroup);
684 m_debugger->stepOutOfFunction();
685}
686
687void V8DebuggerAgentImpl::setPauseOnExceptions(
688 ErrorString* errorString, const String16& stringPauseState) {
689 if (!checkEnabled(errorString)) return;
690 V8Debugger::PauseOnExceptionsState pauseState;
691 if (stringPauseState == "none") {
692 pauseState = V8Debugger::DontPauseOnExceptions;
693 } else if (stringPauseState == "all") {
694 pauseState = V8Debugger::PauseOnAllExceptions;
695 } else if (stringPauseState == "uncaught") {
696 pauseState = V8Debugger::PauseOnUncaughtExceptions;
697 } else {
698 *errorString = "Unknown pause on exceptions mode: " + stringPauseState;
699 return;
700 }
701 setPauseOnExceptionsImpl(errorString, pauseState);
702}
703
704void V8DebuggerAgentImpl::setPauseOnExceptionsImpl(ErrorString* errorString,
705 int pauseState) {
706 m_debugger->setPauseOnExceptionsState(
707 static_cast<V8Debugger::PauseOnExceptionsState>(pauseState));
708 if (m_debugger->getPauseOnExceptionsState() != pauseState)
709 *errorString = "Internal error. Could not change pause on exceptions state";
710 else
711 m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState, pauseState);
712}
713
714void V8DebuggerAgentImpl::evaluateOnCallFrame(
715 ErrorString* errorString, const String16& callFrameId,
716 const String16& expression, const Maybe<String16>& objectGroup,
717 const Maybe<bool>& includeCommandLineAPI, const Maybe<bool>& silent,
718 const Maybe<bool>& returnByValue, const Maybe<bool>& generatePreview,
719 std::unique_ptr<RemoteObject>* result,
720 Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
721 if (!assertPaused(errorString)) return;
722 InjectedScript::CallFrameScope scope(
723 errorString, m_inspector, m_session->contextGroupId(), callFrameId);
724 if (!scope.initialize()) return;
725 if (scope.frameOrdinal() >= m_pausedCallFrames.size()) {
726 *errorString = "Could not find call frame with given id";
727 return;
728 }
729
730 if (includeCommandLineAPI.fromMaybe(false) && !scope.installCommandLineAPI())
731 return;
732 if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole();
733
734 v8::MaybeLocal<v8::Value> maybeResultValue =
735 m_pausedCallFrames[scope.frameOrdinal()]->evaluate(
736 toV8String(m_isolate, expression));
737
738 // Re-initialize after running client's code, as it could have destroyed
739 // context or session.
740 if (!scope.initialize()) return;
741 scope.injectedScript()->wrapEvaluateResult(
742 errorString, maybeResultValue, scope.tryCatch(),
743 objectGroup.fromMaybe(""), returnByValue.fromMaybe(false),
744 generatePreview.fromMaybe(false), result, exceptionDetails);
745}
746
747void V8DebuggerAgentImpl::setVariableValue(
748 ErrorString* errorString, int scopeNumber, const String16& variableName,
749 std::unique_ptr<protocol::Runtime::CallArgument> newValueArgument,
750 const String16& callFrameId) {
751 if (!checkEnabled(errorString)) return;
752 if (!assertPaused(errorString)) return;
753 InjectedScript::CallFrameScope scope(
754 errorString, m_inspector, m_session->contextGroupId(), callFrameId);
755 if (!scope.initialize()) return;
756
757 v8::Local<v8::Value> newValue;
758 if (!scope.injectedScript()
759 ->resolveCallArgument(errorString, newValueArgument.get())
760 .ToLocal(&newValue))
761 return;
762
763 if (scope.frameOrdinal() >= m_pausedCallFrames.size()) {
764 *errorString = "Could not find call frame with given id";
765 return;
766 }
767 v8::MaybeLocal<v8::Value> result =
768 m_pausedCallFrames[scope.frameOrdinal()]->setVariableValue(
769 scopeNumber, toV8String(m_isolate, variableName), newValue);
770 if (scope.tryCatch().HasCaught() || result.IsEmpty()) {
771 *errorString = "Internal error";
772 return;
773 }
774}
775
776void V8DebuggerAgentImpl::setAsyncCallStackDepth(ErrorString* errorString,
777 int depth) {
778 if (!checkEnabled(errorString)) return;
779 m_state->setInteger(DebuggerAgentState::asyncCallStackDepth, depth);
780 m_debugger->setAsyncCallStackDepth(this, depth);
781}
782
783void V8DebuggerAgentImpl::setBlackboxPatterns(
784 ErrorString* errorString,
785 std::unique_ptr<protocol::Array<String16>> patterns) {
786 if (!patterns->length()) {
787 m_blackboxPattern = nullptr;
788 m_state->remove(DebuggerAgentState::blackboxPattern);
789 return;
790 }
791
792 String16Builder patternBuilder;
793 patternBuilder.append('(');
794 for (size_t i = 0; i < patterns->length() - 1; ++i) {
795 patternBuilder.append(patterns->get(i));
796 patternBuilder.append("|");
797 }
798 patternBuilder.append(patterns->get(patterns->length() - 1));
799 patternBuilder.append(')');
800 String16 pattern = patternBuilder.toString();
801 if (!setBlackboxPattern(errorString, pattern)) return;
802 m_state->setString(DebuggerAgentState::blackboxPattern, pattern);
803}
804
805bool V8DebuggerAgentImpl::setBlackboxPattern(ErrorString* errorString,
806 const String16& pattern) {
807 std::unique_ptr<V8Regex> regex(new V8Regex(
808 m_inspector, pattern, true /** caseSensitive */, false /** multiline */));
809 if (!regex->isValid()) {
810 *errorString = "Pattern parser error: " + regex->errorMessage();
811 return false;
812 }
813 m_blackboxPattern = std::move(regex);
814 return true;
815}
816
817void V8DebuggerAgentImpl::setBlackboxedRanges(
818 ErrorString* error, const String16& scriptId,
819 std::unique_ptr<protocol::Array<protocol::Debugger::ScriptPosition>>
820 inPositions) {
821 if (m_scripts.find(scriptId) == m_scripts.end()) {
822 *error = "No script with passed id.";
823 return;
824 }
825
826 if (!inPositions->length()) {
827 m_blackboxedPositions.erase(scriptId);
828 return;
829 }
830
831 std::vector<std::pair<int, int>> positions;
832 positions.reserve(inPositions->length());
833 for (size_t i = 0; i < inPositions->length(); ++i) {
834 protocol::Debugger::ScriptPosition* position = inPositions->get(i);
835 if (position->getLineNumber() < 0) {
836 *error = "Position missing 'line' or 'line' < 0.";
837 return;
838 }
839 if (position->getColumnNumber() < 0) {
840 *error = "Position missing 'column' or 'column' < 0.";
841 return;
842 }
843 positions.push_back(
844 std::make_pair(position->getLineNumber(), position->getColumnNumber()));
845 }
846
847 for (size_t i = 1; i < positions.size(); ++i) {
848 if (positions[i - 1].first < positions[i].first) continue;
849 if (positions[i - 1].first == positions[i].first &&
850 positions[i - 1].second < positions[i].second)
851 continue;
852 *error =
853 "Input positions array is not sorted or contains duplicate values.";
854 return;
855 }
856
857 m_blackboxedPositions[scriptId] = positions;
858}
859
860void V8DebuggerAgentImpl::willExecuteScript(int scriptId) {
861 changeJavaScriptRecursionLevel(+1);
862 // Fast return.
863 if (m_scheduledDebuggerStep != StepInto) return;
864 schedulePauseOnNextStatementIfSteppingInto();
865}
866
867void V8DebuggerAgentImpl::didExecuteScript() {
868 changeJavaScriptRecursionLevel(-1);
869}
870
871void V8DebuggerAgentImpl::changeJavaScriptRecursionLevel(int step) {
872 if (m_javaScriptPauseScheduled && !m_skipAllPauses &&
873 !m_debugger->isPaused()) {
874 // Do not ever loose user's pause request until we have actually paused.
875 m_debugger->setPauseOnNextStatement(true);
876 }
877 if (m_scheduledDebuggerStep == StepOut) {
878 m_recursionLevelForStepOut += step;
879 if (!m_recursionLevelForStepOut) {
880 // When StepOut crosses a task boundary (i.e. js -> c++) from where it was
881 // requested,
882 // switch stepping to step into a next JS task, as if we exited to a
883 // blackboxed framework.
884 m_scheduledDebuggerStep = StepInto;
885 m_skipNextDebuggerStepOut = false;
886 }
887 }
888 if (m_recursionLevelForStepFrame) {
889 m_recursionLevelForStepFrame += step;
890 if (!m_recursionLevelForStepFrame) {
891 // We have walked through a blackboxed framework and got back to where we
892 // started.
893 // If there was no stepping scheduled, we should cancel the stepping
894 // explicitly,
895 // since there may be a scheduled StepFrame left.
896 // Otherwise, if we were stepping in/over, the StepFrame will stop at the
897 // right location,
898 // whereas if we were stepping out, we should continue doing so after
899 // debugger pauses
900 // from the old StepFrame.
901 m_skippedStepFrameCount = 0;
902 if (m_scheduledDebuggerStep == NoStep)
903 m_debugger->clearStepping();
904 else if (m_scheduledDebuggerStep == StepOut)
905 m_skipNextDebuggerStepOut = true;
906 }
907 }
908}
909
910std::unique_ptr<Array<CallFrame>> V8DebuggerAgentImpl::currentCallFrames(
911 ErrorString* errorString) {
912 if (m_pausedContext.IsEmpty() || !m_pausedCallFrames.size())
913 return Array<CallFrame>::create();
914 ErrorString ignored;
915 v8::HandleScope handles(m_isolate);
916 v8::Local<v8::Context> debuggerContext =
917 v8::Debug::GetDebugContext(m_isolate);
918 v8::Context::Scope contextScope(debuggerContext);
919
920 v8::Local<v8::Array> objects = v8::Array::New(m_isolate);
921
922 for (size_t frameOrdinal = 0; frameOrdinal < m_pausedCallFrames.size();
923 ++frameOrdinal) {
924 const std::unique_ptr<JavaScriptCallFrame>& currentCallFrame =
925 m_pausedCallFrames[frameOrdinal];
926
927 v8::Local<v8::Object> details = currentCallFrame->details();
928 if (hasInternalError(errorString, details.IsEmpty()))
929 return Array<CallFrame>::create();
930
931 int contextId = currentCallFrame->contextId();
932 InjectedScript* injectedScript =
933 contextId ? m_session->findInjectedScript(&ignored, contextId)
934 : nullptr;
935
936 String16 callFrameId =
937 RemoteCallFrameId::serialize(contextId, static_cast<int>(frameOrdinal));
938 if (hasInternalError(
939 errorString,
940 !details
941 ->Set(debuggerContext,
942 toV8StringInternalized(m_isolate, "callFrameId"),
943 toV8String(m_isolate, callFrameId))
944 .FromMaybe(false)))
945 return Array<CallFrame>::create();
946
947 if (injectedScript) {
948 v8::Local<v8::Value> scopeChain;
949 if (hasInternalError(
950 errorString,
951 !details->Get(debuggerContext,
952 toV8StringInternalized(m_isolate, "scopeChain"))
953 .ToLocal(&scopeChain) ||
954 !scopeChain->IsArray()))
955 return Array<CallFrame>::create();
956 v8::Local<v8::Array> scopeChainArray = scopeChain.As<v8::Array>();
957 if (!injectedScript->wrapPropertyInArray(
958 errorString, scopeChainArray,
959 toV8StringInternalized(m_isolate, "object"),
960 backtraceObjectGroup))
961 return Array<CallFrame>::create();
962 if (!injectedScript->wrapObjectProperty(
963 errorString, details, toV8StringInternalized(m_isolate, "this"),
964 backtraceObjectGroup))
965 return Array<CallFrame>::create();
966 if (details
967 ->Has(debuggerContext,
968 toV8StringInternalized(m_isolate, "returnValue"))
969 .FromMaybe(false)) {
970 if (!injectedScript->wrapObjectProperty(
971 errorString, details,
972 toV8StringInternalized(m_isolate, "returnValue"),
973 backtraceObjectGroup))
974 return Array<CallFrame>::create();
975 }
976 } else {
977 if (hasInternalError(errorString, !details
978 ->Set(debuggerContext,
979 toV8StringInternalized(
980 m_isolate, "scopeChain"),
981 v8::Array::New(m_isolate, 0))
982 .FromMaybe(false)))
983 return Array<CallFrame>::create();
984 v8::Local<v8::Object> remoteObject = v8::Object::New(m_isolate);
985 if (hasInternalError(
986 errorString,
987 !remoteObject
988 ->Set(debuggerContext,
989 toV8StringInternalized(m_isolate, "type"),
990 toV8StringInternalized(m_isolate, "undefined"))
991 .FromMaybe(false)))
992 return Array<CallFrame>::create();
993 if (hasInternalError(errorString,
994 !details
995 ->Set(debuggerContext,
996 toV8StringInternalized(m_isolate, "this"),
997 remoteObject)
998 .FromMaybe(false)))
999 return Array<CallFrame>::create();
1000 if (hasInternalError(
1001 errorString,
1002 !details
1003 ->Delete(debuggerContext,
1004 toV8StringInternalized(m_isolate, "returnValue"))
1005 .FromMaybe(false)))
1006 return Array<CallFrame>::create();
1007 }
1008
1009 if (hasInternalError(
1010 errorString,
1011 !objects
1012 ->Set(debuggerContext, static_cast<int>(frameOrdinal), details)
1013 .FromMaybe(false)))
1014 return Array<CallFrame>::create();
1015 }
1016
1017 std::unique_ptr<protocol::Value> protocolValue =
1018 toProtocolValue(errorString, debuggerContext, objects);
1019 if (!protocolValue) return Array<CallFrame>::create();
1020 protocol::ErrorSupport errorSupport;
1021 std::unique_ptr<Array<CallFrame>> callFrames =
1022 Array<CallFrame>::parse(protocolValue.get(), &errorSupport);
1023 if (hasInternalError(errorString, !callFrames))
1024 return Array<CallFrame>::create();
1025 return callFrames;
1026}
1027
1028std::unique_ptr<StackTrace> V8DebuggerAgentImpl::currentAsyncStackTrace() {
1029 if (m_pausedContext.IsEmpty()) return nullptr;
1030 V8StackTraceImpl* stackTrace = m_debugger->currentAsyncCallChain();
1031 return stackTrace ? stackTrace->buildInspectorObjectForTail(m_debugger)
1032 : nullptr;
1033}
1034
1035void V8DebuggerAgentImpl::didParseSource(
1036 std::unique_ptr<V8DebuggerScript> script, bool success) {
1037 v8::HandleScope handles(m_isolate);
1038 String16 scriptSource = toProtocolString(script->source(m_isolate));
1039 if (!success) script->setSourceURL(findSourceURL(scriptSource, false));
1040 if (!success)
1041 script->setSourceMappingURL(findSourceMapURL(scriptSource, false));
1042
1043 std::unique_ptr<protocol::DictionaryValue> executionContextAuxData;
1044 if (!script->executionContextAuxData().isEmpty())
1045 executionContextAuxData = protocol::DictionaryValue::cast(
1046 protocol::parseJSON(script->executionContextAuxData()));
1047 bool isLiveEdit = script->isLiveEdit();
1048 bool hasSourceURL = script->hasSourceURL();
1049 String16 scriptId = script->scriptId();
1050 String16 scriptURL = script->sourceURL();
1051
1052 const Maybe<String16>& sourceMapURLParam = script->sourceMappingURL();
1053 const Maybe<protocol::DictionaryValue>& executionContextAuxDataParam(
1054 std::move(executionContextAuxData));
1055 const bool* isLiveEditParam = isLiveEdit ? &isLiveEdit : nullptr;
1056 const bool* hasSourceURLParam = hasSourceURL ? &hasSourceURL : nullptr;
1057 if (success)
1058 m_frontend.scriptParsed(
1059 scriptId, scriptURL, script->startLine(), script->startColumn(),
1060 script->endLine(), script->endColumn(), script->executionContextId(),
1061 script->hash(), executionContextAuxDataParam, isLiveEditParam,
1062 sourceMapURLParam, hasSourceURLParam);
1063 else
1064 m_frontend.scriptFailedToParse(
1065 scriptId, scriptURL, script->startLine(), script->startColumn(),
1066 script->endLine(), script->endColumn(), script->executionContextId(),
1067 script->hash(), executionContextAuxDataParam, sourceMapURLParam,
1068 hasSourceURLParam);
1069
1070 m_scripts[scriptId] = std::move(script);
1071
1072 if (scriptURL.isEmpty() || !success) return;
1073
1074 protocol::DictionaryValue* breakpointsCookie =
1075 m_state->getObject(DebuggerAgentState::javaScriptBreakpoints);
1076 if (!breakpointsCookie) return;
1077
1078 for (size_t i = 0; i < breakpointsCookie->size(); ++i) {
1079 auto cookie = breakpointsCookie->at(i);
1080 protocol::DictionaryValue* breakpointObject =
1081 protocol::DictionaryValue::cast(cookie.second);
1082 bool isRegex;
1083 breakpointObject->getBoolean(DebuggerAgentState::isRegex, &isRegex);
1084 String16 url;
1085 breakpointObject->getString(DebuggerAgentState::url, &url);
1086 if (!matches(m_inspector, scriptURL, url, isRegex)) continue;
1087 ScriptBreakpoint breakpoint;
1088 breakpointObject->getInteger(DebuggerAgentState::lineNumber,
1089 &breakpoint.lineNumber);
1090 breakpointObject->getInteger(DebuggerAgentState::columnNumber,
1091 &breakpoint.columnNumber);
1092 breakpointObject->getString(DebuggerAgentState::condition,
1093 &breakpoint.condition);
1094 std::unique_ptr<protocol::Debugger::Location> location = resolveBreakpoint(
1095 cookie.first, scriptId, breakpoint, UserBreakpointSource);
1096 if (location)
1097 m_frontend.breakpointResolved(cookie.first, std::move(location));
1098 }
1099}
1100
1101V8DebuggerAgentImpl::SkipPauseRequest V8DebuggerAgentImpl::didPause(
1102 v8::Local<v8::Context> context, v8::Local<v8::Value> exception,
1103 const std::vector<String16>& hitBreakpoints, bool isPromiseRejection) {
1104 JavaScriptCallFrames callFrames = m_debugger->currentCallFrames(1);
1105 JavaScriptCallFrame* topCallFrame =
1106 !callFrames.empty() ? callFrames.begin()->get() : nullptr;
1107
1108 V8DebuggerAgentImpl::SkipPauseRequest result;
1109 if (m_skipAllPauses)
1110 result = RequestContinue;
1111 else if (!hitBreakpoints.empty())
1112 result = RequestNoSkip; // Don't skip explicit breakpoints even if set in
1113 // frameworks.
1114 else if (!exception.IsEmpty())
1115 result = shouldSkipExceptionPause(topCallFrame);
1116 else if (m_scheduledDebuggerStep != NoStep || m_javaScriptPauseScheduled ||
1117 m_pausingOnNativeEvent)
1118 result = shouldSkipStepPause(topCallFrame);
1119 else
1120 result = RequestNoSkip;
1121
1122 m_skipNextDebuggerStepOut = false;
1123 if (result != RequestNoSkip) return result;
1124 // Skip pauses inside V8 internal scripts and on syntax errors.
1125 if (!topCallFrame) return RequestContinue;
1126
1127 DCHECK(m_pausedContext.IsEmpty());
1128 JavaScriptCallFrames frames = m_debugger->currentCallFrames();
1129 m_pausedCallFrames.swap(frames);
1130 m_pausedContext.Reset(m_isolate, context);
1131 v8::HandleScope handles(m_isolate);
1132
1133 if (!exception.IsEmpty()) {
1134 ErrorString ignored;
1135 InjectedScript* injectedScript =
1136 m_session->findInjectedScript(&ignored, V8Debugger::contextId(context));
1137 if (injectedScript) {
1138 m_breakReason =
1139 isPromiseRejection
1140 ? protocol::Debugger::Paused::ReasonEnum::PromiseRejection
1141 : protocol::Debugger::Paused::ReasonEnum::Exception;
1142 ErrorString errorString;
1143 auto obj = injectedScript->wrapObject(&errorString, exception,
1144 backtraceObjectGroup);
1145 m_breakAuxData = obj ? obj->serialize() : nullptr;
1146 // m_breakAuxData might be null after this.
1147 }
1148 }
1149
1150 std::unique_ptr<Array<String16>> hitBreakpointIds = Array<String16>::create();
1151
1152 for (const auto& point : hitBreakpoints) {
1153 DebugServerBreakpointToBreakpointIdAndSourceMap::iterator
1154 breakpointIterator = m_serverBreakpoints.find(point);
1155 if (breakpointIterator != m_serverBreakpoints.end()) {
1156 const String16& localId = breakpointIterator->second.first;
1157 hitBreakpointIds->addItem(localId);
1158
1159 BreakpointSource source = breakpointIterator->second.second;
1160 if (m_breakReason == protocol::Debugger::Paused::ReasonEnum::Other &&
1161 source == DebugCommandBreakpointSource)
1162 m_breakReason = protocol::Debugger::Paused::ReasonEnum::DebugCommand;
1163 }
1164 }
1165
1166 ErrorString errorString;
1167 m_frontend.paused(currentCallFrames(&errorString), m_breakReason,
1168 std::move(m_breakAuxData), std::move(hitBreakpointIds),
1169 currentAsyncStackTrace());
1170 m_scheduledDebuggerStep = NoStep;
1171 m_javaScriptPauseScheduled = false;
1172 m_steppingFromFramework = false;
1173 m_pausingOnNativeEvent = false;
1174 m_skippedStepFrameCount = 0;
1175 m_recursionLevelForStepFrame = 0;
1176
1177 if (!m_continueToLocationBreakpointId.isEmpty()) {
1178 m_debugger->removeBreakpoint(m_continueToLocationBreakpointId);
1179 m_continueToLocationBreakpointId = "";
1180 }
1181 return result;
1182}
1183
1184void V8DebuggerAgentImpl::didContinue() {
1185 m_pausedContext.Reset();
1186 JavaScriptCallFrames emptyCallFrames;
1187 m_pausedCallFrames.swap(emptyCallFrames);
1188 clearBreakDetails();
1189 m_frontend.resumed();
1190}
1191
1192void V8DebuggerAgentImpl::breakProgram(
1193 const String16& breakReason,
1194 std::unique_ptr<protocol::DictionaryValue> data) {
1195 if (!enabled() || m_skipAllPauses || !m_pausedContext.IsEmpty() ||
1196 isCurrentCallStackEmptyOrBlackboxed() ||
1197 !m_debugger->breakpointsActivated())
1198 return;
1199 m_breakReason = breakReason;
1200 m_breakAuxData = std::move(data);
1201 m_scheduledDebuggerStep = NoStep;
1202 m_steppingFromFramework = false;
1203 m_pausingOnNativeEvent = false;
1204 m_debugger->breakProgram();
1205}
1206
1207void V8DebuggerAgentImpl::breakProgramOnException(
1208 const String16& breakReason,
1209 std::unique_ptr<protocol::DictionaryValue> data) {
1210 if (!enabled() ||
1211 m_debugger->getPauseOnExceptionsState() ==
1212 V8Debugger::DontPauseOnExceptions)
1213 return;
1214 breakProgram(breakReason, std::move(data));
1215}
1216
1217bool V8DebuggerAgentImpl::assertPaused(ErrorString* errorString) {
1218 if (m_pausedContext.IsEmpty()) {
1219 *errorString = "Can only perform operation while paused.";
1220 return false;
1221 }
1222 return true;
1223}
1224
1225void V8DebuggerAgentImpl::clearBreakDetails() {
1226 m_breakReason = protocol::Debugger::Paused::ReasonEnum::Other;
1227 m_breakAuxData = nullptr;
1228}
1229
1230void V8DebuggerAgentImpl::setBreakpointAt(const String16& scriptId,
1231 int lineNumber, int columnNumber,
1232 BreakpointSource source,
1233 const String16& condition) {
1234 String16 breakpointId =
1235 generateBreakpointId(scriptId, lineNumber, columnNumber, source);
1236 ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition);
1237 resolveBreakpoint(breakpointId, scriptId, breakpoint, source);
1238}
1239
1240void V8DebuggerAgentImpl::removeBreakpointAt(const String16& scriptId,
1241 int lineNumber, int columnNumber,
1242 BreakpointSource source) {
1243 removeBreakpoint(
1244 generateBreakpointId(scriptId, lineNumber, columnNumber, source));
1245}
1246
1247void V8DebuggerAgentImpl::reset() {
1248 if (!enabled()) return;
1249 m_scheduledDebuggerStep = NoStep;
1250 m_scripts.clear();
1251 m_blackboxedPositions.clear();
1252 m_breakpointIdToDebuggerBreakpointIds.clear();
1253}
1254
1255} // namespace v8_inspector