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

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

Issue 2338413003: [inspector] change implementation file extension from cpp to cc (Closed)
Patch Set: string16 -> string-16 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
23 #include "include/v8-inspector.h"
24
25 #include <algorithm>
26
27 namespace v8_inspector {
28
29 using protocol::Array;
30 using protocol::Maybe;
31 using protocol::Debugger::BreakpointId;
32 using protocol::Debugger::CallFrame;
33 using protocol::Runtime::ExceptionDetails;
34 using protocol::Runtime::ScriptId;
35 using protocol::Runtime::StackTrace;
36 using protocol::Runtime::RemoteObject;
37
38 namespace DebuggerAgentState {
39 static const char javaScriptBreakpoints[] = "javaScriptBreakopints";
40 static const char pauseOnExceptionsState[] = "pauseOnExceptionsState";
41 static const char asyncCallStackDepth[] = "asyncCallStackDepth";
42 static const char blackboxPattern[] = "blackboxPattern";
43 static const char debuggerEnabled[] = "debuggerEnabled";
44
45 // Breakpoint properties.
46 static const char url[] = "url";
47 static const char isRegex[] = "isRegex";
48 static const char lineNumber[] = "lineNumber";
49 static const char columnNumber[] = "columnNumber";
50 static const char condition[] = "condition";
51 static const char skipAllPauses[] = "skipAllPauses";
52
53 } // namespace DebuggerAgentState;
54
55 static const int maxSkipStepFrameCount = 128;
56 static const char backtraceObjectGroup[] = "backtrace";
57
58 static 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
71 static 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
78 static 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
84 static bool hasInternalError(ErrorString* errorString, bool hasError) {
85 if (hasError) *errorString = "Internal error";
86 return hasError;
87 }
88
89 static 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
98 V8DebuggerAgentImpl::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
121 V8DebuggerAgentImpl::~V8DebuggerAgentImpl() {}
122
123 bool V8DebuggerAgentImpl::checkEnabled(ErrorString* errorString) {
124 if (enabled()) return true;
125 *errorString = "Debugger agent is not enabled";
126 return false;
127 }
128
129 void 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
146 bool V8DebuggerAgentImpl::enabled() { return m_enabled; }
147
148 void 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
159 void 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
193 void 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
223 void V8DebuggerAgentImpl::setBreakpointsActive(ErrorString* errorString,
224 bool active) {
225 if (!checkEnabled(errorString)) return;
226 m_debugger->setBreakpointsActivated(active);
227 }
228
229 void V8DebuggerAgentImpl::setSkipAllPauses(ErrorString*, bool skip) {
230 m_skipAllPauses = skip;
231 m_state->setBoolean(DebuggerAgentState::skipAllPauses, m_skipAllPauses);
232 }
233
234 static std::unique_ptr<protocol::DictionaryValue>
235 buildObjectForBreakpointCookie(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
248 static 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
257 void 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
315 static 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
325 void 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
356 void 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
365 void 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
383 void 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
406 bool 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
416 bool V8DebuggerAgentImpl::isTopPausedCallFrameBlackboxed() {
417 DCHECK(enabled());
418 JavaScriptCallFrame* frame =
419 m_pausedCallFrames.size() ? m_pausedCallFrames[0].get() : nullptr;
420 return isCallFrameWithUnknownScriptOrBlackboxed(frame);
421 }
422
423 bool 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
453 V8DebuggerAgentImpl::SkipPauseRequest
454 V8DebuggerAgentImpl::shouldSkipExceptionPause(
455 JavaScriptCallFrame* topCallFrame) {
456 if (m_steppingFromFramework) return RequestNoSkip;
457 if (isCallFrameWithUnknownScriptOrBlackboxed(topCallFrame))
458 return RequestContinue;
459 return RequestNoSkip;
460 }
461
462 V8DebuggerAgentImpl::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
482 std::unique_ptr<protocol::Debugger::Location>
483 V8DebuggerAgentImpl::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
512 void 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
534 void 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
558 void 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
589 void 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
602 void 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
616 void 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
628 void V8DebuggerAgentImpl::cancelPauseOnNextStatement() {
629 if (m_javaScriptPauseScheduled || m_debugger->isPaused()) return;
630 clearBreakDetails();
631 m_pausingOnNativeEvent = false;
632 m_debugger->setPauseOnNextStatement(false);
633 }
634
635 void 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
646 void 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
654 void 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
669 void 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
677 void 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
687 void 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
704 void 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
714 void 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
747 void 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
776 void 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
783 void 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
805 bool 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
817 void 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
860 void V8DebuggerAgentImpl::willExecuteScript(int scriptId) {
861 changeJavaScriptRecursionLevel(+1);
862 // Fast return.
863 if (m_scheduledDebuggerStep != StepInto) return;
864 schedulePauseOnNextStatementIfSteppingInto();
865 }
866
867 void V8DebuggerAgentImpl::didExecuteScript() {
868 changeJavaScriptRecursionLevel(-1);
869 }
870
871 void 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
910 std::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, 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(errorString,
1010 !objects->Set(debuggerContext, frameOrdinal, details)
1011 .FromMaybe(false)))
1012 return Array<CallFrame>::create();
1013 }
1014
1015 protocol::ErrorSupport errorSupport;
1016 std::unique_ptr<Array<CallFrame>> callFrames = Array<CallFrame>::parse(
1017 toProtocolValue(debuggerContext, objects).get(), &errorSupport);
1018 if (hasInternalError(errorString, !callFrames))
1019 return Array<CallFrame>::create();
1020 return callFrames;
1021 }
1022
1023 std::unique_ptr<StackTrace> V8DebuggerAgentImpl::currentAsyncStackTrace() {
1024 if (m_pausedContext.IsEmpty()) return nullptr;
1025 V8StackTraceImpl* stackTrace = m_debugger->currentAsyncCallChain();
1026 return stackTrace ? stackTrace->buildInspectorObjectForTail(m_debugger)
1027 : nullptr;
1028 }
1029
1030 void V8DebuggerAgentImpl::didParseSource(
1031 std::unique_ptr<V8DebuggerScript> script, bool success) {
1032 v8::HandleScope handles(m_isolate);
1033 String16 scriptSource = toProtocolString(script->source(m_isolate));
1034 if (!success) script->setSourceURL(findSourceURL(scriptSource, false));
1035 if (!success)
1036 script->setSourceMappingURL(findSourceMapURL(scriptSource, false));
1037
1038 std::unique_ptr<protocol::DictionaryValue> executionContextAuxData;
1039 if (!script->executionContextAuxData().isEmpty())
1040 executionContextAuxData = protocol::DictionaryValue::cast(
1041 protocol::parseJSON(script->executionContextAuxData()));
1042 bool isLiveEdit = script->isLiveEdit();
1043 bool hasSourceURL = script->hasSourceURL();
1044 String16 scriptId = script->scriptId();
1045 String16 scriptURL = script->sourceURL();
1046
1047 const Maybe<String16>& sourceMapURLParam = script->sourceMappingURL();
1048 const Maybe<protocol::DictionaryValue>& executionContextAuxDataParam(
1049 std::move(executionContextAuxData));
1050 const bool* isLiveEditParam = isLiveEdit ? &isLiveEdit : nullptr;
1051 const bool* hasSourceURLParam = hasSourceURL ? &hasSourceURL : nullptr;
1052 if (success)
1053 m_frontend.scriptParsed(
1054 scriptId, scriptURL, script->startLine(), script->startColumn(),
1055 script->endLine(), script->endColumn(), script->executionContextId(),
1056 script->hash(), executionContextAuxDataParam, isLiveEditParam,
1057 sourceMapURLParam, hasSourceURLParam);
1058 else
1059 m_frontend.scriptFailedToParse(
1060 scriptId, scriptURL, script->startLine(), script->startColumn(),
1061 script->endLine(), script->endColumn(), script->executionContextId(),
1062 script->hash(), executionContextAuxDataParam, sourceMapURLParam,
1063 hasSourceURLParam);
1064
1065 m_scripts[scriptId] = std::move(script);
1066
1067 if (scriptURL.isEmpty() || !success) return;
1068
1069 protocol::DictionaryValue* breakpointsCookie =
1070 m_state->getObject(DebuggerAgentState::javaScriptBreakpoints);
1071 if (!breakpointsCookie) return;
1072
1073 for (size_t i = 0; i < breakpointsCookie->size(); ++i) {
1074 auto cookie = breakpointsCookie->at(i);
1075 protocol::DictionaryValue* breakpointObject =
1076 protocol::DictionaryValue::cast(cookie.second);
1077 bool isRegex;
1078 breakpointObject->getBoolean(DebuggerAgentState::isRegex, &isRegex);
1079 String16 url;
1080 breakpointObject->getString(DebuggerAgentState::url, &url);
1081 if (!matches(m_inspector, scriptURL, url, isRegex)) continue;
1082 ScriptBreakpoint breakpoint;
1083 breakpointObject->getInteger(DebuggerAgentState::lineNumber,
1084 &breakpoint.lineNumber);
1085 breakpointObject->getInteger(DebuggerAgentState::columnNumber,
1086 &breakpoint.columnNumber);
1087 breakpointObject->getString(DebuggerAgentState::condition,
1088 &breakpoint.condition);
1089 std::unique_ptr<protocol::Debugger::Location> location = resolveBreakpoint(
1090 cookie.first, scriptId, breakpoint, UserBreakpointSource);
1091 if (location)
1092 m_frontend.breakpointResolved(cookie.first, std::move(location));
1093 }
1094 }
1095
1096 V8DebuggerAgentImpl::SkipPauseRequest V8DebuggerAgentImpl::didPause(
1097 v8::Local<v8::Context> context, v8::Local<v8::Value> exception,
1098 const std::vector<String16>& hitBreakpoints, bool isPromiseRejection) {
1099 JavaScriptCallFrames callFrames = m_debugger->currentCallFrames(1);
1100 JavaScriptCallFrame* topCallFrame =
1101 !callFrames.empty() ? callFrames.begin()->get() : nullptr;
1102
1103 V8DebuggerAgentImpl::SkipPauseRequest result;
1104 if (m_skipAllPauses)
1105 result = RequestContinue;
1106 else if (!hitBreakpoints.empty())
1107 result = RequestNoSkip; // Don't skip explicit breakpoints even if set in
1108 // frameworks.
1109 else if (!exception.IsEmpty())
1110 result = shouldSkipExceptionPause(topCallFrame);
1111 else if (m_scheduledDebuggerStep != NoStep || m_javaScriptPauseScheduled ||
1112 m_pausingOnNativeEvent)
1113 result = shouldSkipStepPause(topCallFrame);
1114 else
1115 result = RequestNoSkip;
1116
1117 m_skipNextDebuggerStepOut = false;
1118 if (result != RequestNoSkip) return result;
1119 // Skip pauses inside V8 internal scripts and on syntax errors.
1120 if (!topCallFrame) return RequestContinue;
1121
1122 DCHECK(m_pausedContext.IsEmpty());
1123 JavaScriptCallFrames frames = m_debugger->currentCallFrames();
1124 m_pausedCallFrames.swap(frames);
1125 m_pausedContext.Reset(m_isolate, context);
1126 v8::HandleScope handles(m_isolate);
1127
1128 if (!exception.IsEmpty()) {
1129 ErrorString ignored;
1130 InjectedScript* injectedScript =
1131 m_session->findInjectedScript(&ignored, V8Debugger::contextId(context));
1132 if (injectedScript) {
1133 m_breakReason =
1134 isPromiseRejection
1135 ? protocol::Debugger::Paused::ReasonEnum::PromiseRejection
1136 : protocol::Debugger::Paused::ReasonEnum::Exception;
1137 ErrorString errorString;
1138 auto obj = injectedScript->wrapObject(&errorString, exception,
1139 backtraceObjectGroup);
1140 m_breakAuxData = obj ? obj->serialize() : nullptr;
1141 // m_breakAuxData might be null after this.
1142 }
1143 }
1144
1145 std::unique_ptr<Array<String16>> hitBreakpointIds = Array<String16>::create();
1146
1147 for (const auto& point : hitBreakpoints) {
1148 DebugServerBreakpointToBreakpointIdAndSourceMap::iterator
1149 breakpointIterator = m_serverBreakpoints.find(point);
1150 if (breakpointIterator != m_serverBreakpoints.end()) {
1151 const String16& localId = breakpointIterator->second.first;
1152 hitBreakpointIds->addItem(localId);
1153
1154 BreakpointSource source = breakpointIterator->second.second;
1155 if (m_breakReason == protocol::Debugger::Paused::ReasonEnum::Other &&
1156 source == DebugCommandBreakpointSource)
1157 m_breakReason = protocol::Debugger::Paused::ReasonEnum::DebugCommand;
1158 }
1159 }
1160
1161 ErrorString errorString;
1162 m_frontend.paused(currentCallFrames(&errorString), m_breakReason,
1163 std::move(m_breakAuxData), std::move(hitBreakpointIds),
1164 currentAsyncStackTrace());
1165 m_scheduledDebuggerStep = NoStep;
1166 m_javaScriptPauseScheduled = false;
1167 m_steppingFromFramework = false;
1168 m_pausingOnNativeEvent = false;
1169 m_skippedStepFrameCount = 0;
1170 m_recursionLevelForStepFrame = 0;
1171
1172 if (!m_continueToLocationBreakpointId.isEmpty()) {
1173 m_debugger->removeBreakpoint(m_continueToLocationBreakpointId);
1174 m_continueToLocationBreakpointId = "";
1175 }
1176 return result;
1177 }
1178
1179 void V8DebuggerAgentImpl::didContinue() {
1180 m_pausedContext.Reset();
1181 JavaScriptCallFrames emptyCallFrames;
1182 m_pausedCallFrames.swap(emptyCallFrames);
1183 clearBreakDetails();
1184 m_frontend.resumed();
1185 }
1186
1187 void V8DebuggerAgentImpl::breakProgram(
1188 const String16& breakReason,
1189 std::unique_ptr<protocol::DictionaryValue> data) {
1190 if (!enabled() || m_skipAllPauses || !m_pausedContext.IsEmpty() ||
1191 isCurrentCallStackEmptyOrBlackboxed() ||
1192 !m_debugger->breakpointsActivated())
1193 return;
1194 m_breakReason = breakReason;
1195 m_breakAuxData = std::move(data);
1196 m_scheduledDebuggerStep = NoStep;
1197 m_steppingFromFramework = false;
1198 m_pausingOnNativeEvent = false;
1199 m_debugger->breakProgram();
1200 }
1201
1202 void V8DebuggerAgentImpl::breakProgramOnException(
1203 const String16& breakReason,
1204 std::unique_ptr<protocol::DictionaryValue> data) {
1205 if (!enabled() ||
1206 m_debugger->getPauseOnExceptionsState() ==
1207 V8Debugger::DontPauseOnExceptions)
1208 return;
1209 breakProgram(breakReason, std::move(data));
1210 }
1211
1212 bool V8DebuggerAgentImpl::assertPaused(ErrorString* errorString) {
1213 if (m_pausedContext.IsEmpty()) {
1214 *errorString = "Can only perform operation while paused.";
1215 return false;
1216 }
1217 return true;
1218 }
1219
1220 void V8DebuggerAgentImpl::clearBreakDetails() {
1221 m_breakReason = protocol::Debugger::Paused::ReasonEnum::Other;
1222 m_breakAuxData = nullptr;
1223 }
1224
1225 void V8DebuggerAgentImpl::setBreakpointAt(const String16& scriptId,
1226 int lineNumber, int columnNumber,
1227 BreakpointSource source,
1228 const String16& condition) {
1229 String16 breakpointId =
1230 generateBreakpointId(scriptId, lineNumber, columnNumber, source);
1231 ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition);
1232 resolveBreakpoint(breakpointId, scriptId, breakpoint, source);
1233 }
1234
1235 void V8DebuggerAgentImpl::removeBreakpointAt(const String16& scriptId,
1236 int lineNumber, int columnNumber,
1237 BreakpointSource source) {
1238 removeBreakpoint(
1239 generateBreakpointId(scriptId, lineNumber, columnNumber, source));
1240 }
1241
1242 void V8DebuggerAgentImpl::reset() {
1243 if (!enabled()) return;
1244 m_scheduledDebuggerStep = NoStep;
1245 m_scripts.clear();
1246 m_blackboxedPositions.clear();
1247 m_breakpointIdToDebuggerBreakpointIds.clear();
1248 }
1249
1250 } // 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