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