Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(40)

Side by Side Diff: src/inspector/V8DebuggerAgentImpl.cpp

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

Powered by Google App Engine
This is Rietveld 408576698