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

Side by Side Diff: third_party/WebKit/Source/core/inspector/V8DebuggerAgent.cpp

Issue 1370373004: Revert of DevTools: extract V8DebuggerAgent interface, move it under .../inspector/v8 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 2 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
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium 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 "config.h"
6 #include "core/inspector/V8DebuggerAgent.h"
7
8 #include "bindings/core/v8/ScriptCallStackFactory.h"
9 #include "bindings/core/v8/ScriptRegexp.h"
10 #include "bindings/core/v8/ScriptValue.h"
11 #include "bindings/core/v8/V8Binding.h"
12 #include "bindings/core/v8/V8RecursionScope.h"
13 #include "bindings/core/v8/V8ScriptRunner.h"
14 #include "core/dom/Microtask.h"
15 #include "core/inspector/AsyncCallChain.h"
16 #include "core/inspector/ContentSearchUtils.h"
17 #include "core/inspector/InjectedScript.h"
18 #include "core/inspector/InjectedScriptManager.h"
19 #include "core/inspector/InspectorState.h"
20 #include "core/inspector/InstrumentingAgents.h"
21 #include "core/inspector/JSONParser.h"
22 #include "core/inspector/RemoteObjectId.h"
23 #include "core/inspector/ScriptAsyncCallStack.h"
24 #include "core/inspector/ScriptCallFrame.h"
25 #include "core/inspector/ScriptCallStack.h"
26 #include "core/inspector/V8AsyncCallTracker.h"
27 #include "core/inspector/v8/JavaScriptCallFrame.h"
28 #include "core/inspector/v8/V8Debugger.h"
29 #include "core/inspector/v8/V8JavaScriptCallFrame.h"
30 #include "platform/JSONValues.h"
31 #include "wtf/text/StringBuilder.h"
32 #include "wtf/text/WTFString.h"
33
34 using blink::TypeBuilder::Array;
35 using blink::TypeBuilder::Console::AsyncStackTrace;
36 using blink::TypeBuilder::Debugger::AsyncOperation;
37 using blink::TypeBuilder::Debugger::BreakpointId;
38 using blink::TypeBuilder::Debugger::CallFrame;
39 using blink::TypeBuilder::Debugger::CollectionEntry;
40 using blink::TypeBuilder::Debugger::ExceptionDetails;
41 using blink::TypeBuilder::Debugger::FunctionDetails;
42 using blink::TypeBuilder::Debugger::GeneratorObjectDetails;
43 using blink::TypeBuilder::Debugger::PromiseDetails;
44 using blink::TypeBuilder::Debugger::ScriptId;
45 using blink::TypeBuilder::Debugger::StackTrace;
46 using blink::TypeBuilder::Runtime::RemoteObject;
47
48 namespace blink {
49
50 namespace DebuggerAgentState {
51 static const char debuggerEnabled[] = "debuggerEnabled";
52 static const char javaScriptBreakpoints[] = "javaScriptBreakopints";
53 static const char pauseOnExceptionsState[] = "pauseOnExceptionsState";
54 static const char asyncCallStackDepth[] = "asyncCallStackDepth";
55 static const char promiseTrackerEnabled[] = "promiseTrackerEnabled";
56 static const char promiseTrackerCaptureStacks[] = "promiseTrackerCaptureStacks";
57
58 // Breakpoint properties.
59 static const char url[] = "url";
60 static const char isRegex[] = "isRegex";
61 static const char lineNumber[] = "lineNumber";
62 static const char columnNumber[] = "columnNumber";
63 static const char condition[] = "condition";
64 static const char skipStackPattern[] = "skipStackPattern";
65 static const char skipContentScripts[] = "skipContentScripts";
66 static const char skipAllPauses[] = "skipAllPauses";
67
68 };
69
70 static const int maxSkipStepFrameCount = 128;
71
72 const char V8DebuggerAgent::backtraceObjectGroup[] = "backtrace";
73
74 const int V8DebuggerAgent::unknownAsyncOperationId = 0;
75
76 static String breakpointIdSuffix(V8DebuggerAgent::BreakpointSource source)
77 {
78 switch (source) {
79 case V8DebuggerAgent::UserBreakpointSource:
80 break;
81 case V8DebuggerAgent::DebugCommandBreakpointSource:
82 return ":debug";
83 case V8DebuggerAgent::MonitorCommandBreakpointSource:
84 return ":monitor";
85 }
86 return String();
87 }
88
89 static String generateBreakpointId(const String& scriptId, int lineNumber, int c olumnNumber, V8DebuggerAgent::BreakpointSource source)
90 {
91 return scriptId + ':' + String::number(lineNumber) + ':' + String::number(co lumnNumber) + breakpointIdSuffix(source);
92 }
93
94 static ScriptCallFrame toScriptCallFrame(JavaScriptCallFrame* callFrame)
95 {
96 String scriptId = String::number(callFrame->sourceID());
97 // FIXME(WK62725): Debugger line/column are 0-based, while console ones are 1-based.
98 int line = callFrame->line() + 1;
99 int column = callFrame->column() + 1;
100 return ScriptCallFrame(callFrame->functionName(), scriptId, callFrame->scrip tName(), line, column);
101 }
102
103 static PassRefPtrWillBeRawPtr<ScriptCallStack> toScriptCallStack(JavaScriptCallF rame* callFrame)
104 {
105 Vector<ScriptCallFrame> frames;
106 for (; callFrame; callFrame = callFrame->caller())
107 frames.append(toScriptCallFrame(callFrame));
108 return ScriptCallStack::create(frames);
109 }
110
111 static PassRefPtr<JavaScriptCallFrame> toJavaScriptCallFrame(v8::Local<v8::Objec t> value)
112 {
113 if (value.IsEmpty())
114 return nullptr;
115 return V8JavaScriptCallFrame::unwrap(value);
116 }
117
118 static PassRefPtrWillBeRawPtr<ScriptCallStack> toScriptCallStack(v8::Local<v8::O bject> callFrames)
119 {
120 RefPtr<JavaScriptCallFrame> jsCallFrame = toJavaScriptCallFrame(callFrames);
121 return jsCallFrame ? toScriptCallStack(jsCallFrame.get()) : nullptr;
122 }
123
124 V8DebuggerAgent::V8DebuggerAgent(InjectedScriptManager* injectedScriptManager, V 8Debugger* debugger, V8DebuggerAgent::Client* client, int contextGroupId)
125 : m_injectedScriptManager(injectedScriptManager)
126 , m_debugger(debugger)
127 , m_client(client)
128 , m_contextGroupId(contextGroupId)
129 , m_state(nullptr)
130 , m_frontend(nullptr)
131 , m_isolate(debugger->isolate())
132 , m_pausedScriptState(nullptr)
133 , m_breakReason(InspectorFrontend::Debugger::Reason::Other)
134 , m_scheduledDebuggerStep(NoStep)
135 , m_skipNextDebuggerStepOut(false)
136 , m_javaScriptPauseScheduled(false)
137 , m_steppingFromFramework(false)
138 , m_pausingOnNativeEvent(false)
139 , m_pausingOnAsyncOperation(false)
140 , m_skippedStepFrameCount(0)
141 , m_recursionLevelForStepOut(0)
142 , m_recursionLevelForStepFrame(0)
143 , m_skipAllPauses(false)
144 , m_skipContentScripts(false)
145 , m_cachedSkipStackGeneration(0)
146 , m_lastAsyncOperationId(0)
147 , m_maxAsyncCallStackDepth(0)
148 , m_currentAsyncCallChain(nullptr)
149 , m_nestedAsyncCallCount(0)
150 , m_currentAsyncOperationId(unknownAsyncOperationId)
151 , m_pendingTraceAsyncOperationCompleted(false)
152 , m_startingStepIntoAsync(false)
153 , m_compiledScripts(debugger->isolate())
154 {
155 ASSERT(contextGroupId);
156 m_v8AsyncCallTracker = V8AsyncCallTracker::create(this);
157 m_promiseTracker = PromiseTracker::create(this, m_isolate);
158 clearBreakDetails();
159 }
160
161 V8DebuggerAgent::~V8DebuggerAgent()
162 {
163 }
164
165 DEFINE_TRACE(V8DebuggerAgent)
166 {
167 #if ENABLE(OILPAN)
168 visitor->trace(m_injectedScriptManager);
169 visitor->trace(m_asyncCallTrackingListeners);
170 visitor->trace(m_v8AsyncCallTracker);
171 visitor->trace(m_promiseTracker);
172 visitor->trace(m_asyncOperations);
173 visitor->trace(m_currentAsyncCallChain);
174 #endif
175 }
176
177 bool V8DebuggerAgent::checkEnabled(ErrorString* errorString)
178 {
179 if (enabled())
180 return true;
181 *errorString = "Debugger agent is not enabled";
182 return false;
183 }
184
185 void V8DebuggerAgent::enable()
186 {
187 // debugger().addListener may result in reporting all parsed scripts to
188 // the agent so it should already be in enabled state by then.
189 m_state->setBoolean(DebuggerAgentState::debuggerEnabled, true);
190 debugger().addListener(m_contextGroupId, this);
191 // FIXME(WK44513): breakpoints activated flag should be synchronized between all front-ends
192 debugger().setBreakpointsActivated(true);
193 m_client->debuggerAgentEnabled();
194 }
195
196 bool V8DebuggerAgent::enabled()
197 {
198 return m_state->getBoolean(DebuggerAgentState::debuggerEnabled);
199 }
200
201 void V8DebuggerAgent::enable(ErrorString*)
202 {
203 if (enabled())
204 return;
205
206 enable();
207
208 ASSERT(m_frontend);
209 }
210
211 void V8DebuggerAgent::disable(ErrorString*)
212 {
213 if (!enabled())
214 return;
215
216 m_state->setObject(DebuggerAgentState::javaScriptBreakpoints, JSONObject::cr eate());
217 m_state->setLong(DebuggerAgentState::pauseOnExceptionsState, V8Debugger::Don tPauseOnExceptions);
218 m_state->setString(DebuggerAgentState::skipStackPattern, "");
219 m_state->setBoolean(DebuggerAgentState::skipContentScripts, false);
220 m_state->setLong(DebuggerAgentState::asyncCallStackDepth, 0);
221 m_state->setBoolean(DebuggerAgentState::promiseTrackerEnabled, false);
222 m_state->setBoolean(DebuggerAgentState::promiseTrackerCaptureStacks, false);
223
224 debugger().removeListener(m_contextGroupId);
225 m_client->debuggerAgentDisabled();
226 m_pausedScriptState = nullptr;
227 m_currentCallStack.Reset();
228 m_scripts.clear();
229 m_breakpointIdToDebuggerBreakpointIds.clear();
230 internalSetAsyncCallStackDepth(0);
231 m_promiseTracker->setEnabled(false, false);
232 m_continueToLocationBreakpointId = String();
233 clearBreakDetails();
234 m_scheduledDebuggerStep = NoStep;
235 m_skipNextDebuggerStepOut = false;
236 m_javaScriptPauseScheduled = false;
237 m_steppingFromFramework = false;
238 m_pausingOnNativeEvent = false;
239 m_skippedStepFrameCount = 0;
240 m_recursionLevelForStepFrame = 0;
241 m_asyncOperationNotifications.clear();
242 m_compiledScripts.Clear();
243 clearStepIntoAsync();
244 m_skipAllPauses = false;
245 m_state->setBoolean(DebuggerAgentState::debuggerEnabled, false);
246 }
247
248 static PassOwnPtr<ScriptRegexp> compileSkipCallFramePattern(String patternText)
249 {
250 if (patternText.isEmpty())
251 return nullptr;
252 OwnPtr<ScriptRegexp> result = adoptPtr(new ScriptRegexp(patternText, TextCas eSensitive));
253 if (!result->isValid())
254 result.clear();
255 return result.release();
256 }
257
258 void V8DebuggerAgent::increaseCachedSkipStackGeneration()
259 {
260 ++m_cachedSkipStackGeneration;
261 if (!m_cachedSkipStackGeneration)
262 m_cachedSkipStackGeneration = 1;
263 }
264
265 void V8DebuggerAgent::internalSetAsyncCallStackDepth(int depth)
266 {
267 if (depth <= 0) {
268 m_maxAsyncCallStackDepth = 0;
269 resetAsyncCallTracker();
270 } else {
271 m_maxAsyncCallStackDepth = depth;
272 }
273 for (auto& listener: m_asyncCallTrackingListeners)
274 listener->asyncCallTrackingStateChanged(m_maxAsyncCallStackDepth);
275 }
276
277 void V8DebuggerAgent::clearFrontend()
278 {
279 ErrorString error;
280 disable(&error);
281 ASSERT(m_frontend);
282 m_frontend = nullptr;
283 }
284
285 void V8DebuggerAgent::restore()
286 {
287 if (enabled()) {
288 m_frontend->globalObjectCleared();
289 enable();
290 long pauseState = m_state->getLong(DebuggerAgentState::pauseOnExceptions State, V8Debugger::DontPauseOnExceptions);
291 String error;
292 setPauseOnExceptionsImpl(&error, pauseState);
293 m_cachedSkipStackRegExp = compileSkipCallFramePattern(m_state->getString (DebuggerAgentState::skipStackPattern));
294 increaseCachedSkipStackGeneration();
295 m_skipContentScripts = m_state->getBoolean(DebuggerAgentState::skipConte ntScripts);
296 m_skipAllPauses = m_state->getBoolean(DebuggerAgentState::skipAllPauses) ;
297 internalSetAsyncCallStackDepth(m_state->getLong(DebuggerAgentState::asyn cCallStackDepth));
298 m_promiseTracker->setEnabled(m_state->getBoolean(DebuggerAgentState::pro miseTrackerEnabled), m_state->getBoolean(DebuggerAgentState::promiseTrackerCaptu reStacks));
299 }
300 }
301
302 void V8DebuggerAgent::setBreakpointsActive(ErrorString* errorString, bool active )
303 {
304 if (!checkEnabled(errorString))
305 return;
306 debugger().setBreakpointsActivated(active);
307 }
308
309 void V8DebuggerAgent::setSkipAllPauses(ErrorString*, bool skipped)
310 {
311 m_skipAllPauses = skipped;
312 m_state->setBoolean(DebuggerAgentState::skipAllPauses, m_skipAllPauses);
313 }
314
315 bool V8DebuggerAgent::isPaused()
316 {
317 return debugger().isPaused();
318 }
319
320 static PassRefPtr<JSONObject> buildObjectForBreakpointCookie(const String& url, int lineNumber, int columnNumber, const String& condition, bool isRegex)
321 {
322 RefPtr<JSONObject> breakpointObject = JSONObject::create();
323 breakpointObject->setString(DebuggerAgentState::url, url);
324 breakpointObject->setNumber(DebuggerAgentState::lineNumber, lineNumber);
325 breakpointObject->setNumber(DebuggerAgentState::columnNumber, columnNumber);
326 breakpointObject->setString(DebuggerAgentState::condition, condition);
327 breakpointObject->setBoolean(DebuggerAgentState::isRegex, isRegex);
328 return breakpointObject.release();
329 }
330
331 static bool matches(const String& url, const String& pattern, bool isRegex)
332 {
333 if (isRegex) {
334 ScriptRegexp regex(pattern, TextCaseSensitive);
335 return regex.match(url) != -1;
336 }
337 return url == pattern;
338 }
339
340 void V8DebuggerAgent::setBreakpointByUrl(ErrorString* errorString, int lineNumbe r, const String* const optionalURL, const String* const optionalURLRegex, const int* const optionalColumnNumber, const String* const optionalCondition, Breakpoi ntId* outBreakpointId, RefPtr<Array<TypeBuilder::Debugger::Location>>& locations )
341 {
342 locations = Array<TypeBuilder::Debugger::Location>::create();
343 if (!optionalURL == !optionalURLRegex) {
344 *errorString = "Either url or urlRegex must be specified.";
345 return;
346 }
347
348 String url = optionalURL ? *optionalURL : *optionalURLRegex;
349 int columnNumber = 0;
350 if (optionalColumnNumber) {
351 columnNumber = *optionalColumnNumber;
352 if (columnNumber < 0) {
353 *errorString = "Incorrect column number";
354 return;
355 }
356 }
357 String condition = optionalCondition ? *optionalCondition : "";
358 bool isRegex = optionalURLRegex;
359
360 String breakpointId = (isRegex ? "/" + url + "/" : url) + ':' + String::numb er(lineNumber) + ':' + String::number(columnNumber);
361 RefPtr<JSONObject> breakpointsCookie = m_state->getObject(DebuggerAgentState ::javaScriptBreakpoints);
362 if (breakpointsCookie->find(breakpointId) != breakpointsCookie->end()) {
363 *errorString = "Breakpoint at specified location already exists.";
364 return;
365 }
366
367 breakpointsCookie->setObject(breakpointId, buildObjectForBreakpointCookie(ur l, lineNumber, columnNumber, condition, isRegex));
368 m_state->setObject(DebuggerAgentState::javaScriptBreakpoints, breakpointsCoo kie);
369
370 ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition);
371 for (auto& script : m_scripts) {
372 if (!matches(script.value.sourceURL(), url, isRegex))
373 continue;
374 RefPtr<TypeBuilder::Debugger::Location> location = resolveBreakpoint(bre akpointId, script.key, breakpoint, UserBreakpointSource);
375 if (location)
376 locations->addItem(location);
377 }
378
379 *outBreakpointId = breakpointId;
380 }
381
382 static bool parseLocation(ErrorString* errorString, PassRefPtr<JSONObject> locat ion, String* scriptId, int* lineNumber, int* columnNumber)
383 {
384 if (!location->getString("scriptId", scriptId) || !location->getNumber("line Number", lineNumber)) {
385 // FIXME: replace with input validation.
386 *errorString = "scriptId and lineNumber are required.";
387 return false;
388 }
389 *columnNumber = 0;
390 location->getNumber("columnNumber", columnNumber);
391 return true;
392 }
393
394 void V8DebuggerAgent::setBreakpoint(ErrorString* errorString, const RefPtr<JSONO bject>& location, const String* const optionalCondition, BreakpointId* outBreakp ointId, RefPtr<TypeBuilder::Debugger::Location>& actualLocation)
395 {
396 String scriptId;
397 int lineNumber;
398 int columnNumber;
399
400 if (!parseLocation(errorString, location, &scriptId, &lineNumber, &columnNum ber))
401 return;
402
403 String condition = optionalCondition ? *optionalCondition : emptyString();
404
405 String breakpointId = generateBreakpointId(scriptId, lineNumber, columnNumbe r, UserBreakpointSource);
406 if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) != m_breakpoint IdToDebuggerBreakpointIds.end()) {
407 *errorString = "Breakpoint at specified location already exists.";
408 return;
409 }
410 ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition);
411 actualLocation = resolveBreakpoint(breakpointId, scriptId, breakpoint, UserB reakpointSource);
412 if (actualLocation)
413 *outBreakpointId = breakpointId;
414 else
415 *errorString = "Could not resolve breakpoint";
416 }
417
418 void V8DebuggerAgent::removeBreakpoint(ErrorString* errorString, const String& b reakpointId)
419 {
420 if (!checkEnabled(errorString))
421 return;
422 RefPtr<JSONObject> breakpointsCookie = m_state->getObject(DebuggerAgentState ::javaScriptBreakpoints);
423 breakpointsCookie->remove(breakpointId);
424 m_state->setObject(DebuggerAgentState::javaScriptBreakpoints, breakpointsCoo kie);
425 removeBreakpoint(breakpointId);
426 }
427
428 void V8DebuggerAgent::removeBreakpoint(const String& breakpointId)
429 {
430 ASSERT(enabled());
431 BreakpointIdToDebuggerBreakpointIdsMap::iterator debuggerBreakpointIdsIterat or = m_breakpointIdToDebuggerBreakpointIds.find(breakpointId);
432 if (debuggerBreakpointIdsIterator == m_breakpointIdToDebuggerBreakpointIds.e nd())
433 return;
434 for (size_t i = 0; i < debuggerBreakpointIdsIterator->value.size(); ++i) {
435 const String& debuggerBreakpointId = debuggerBreakpointIdsIterator->valu e[i];
436 debugger().removeBreakpoint(debuggerBreakpointId);
437 m_serverBreakpoints.remove(debuggerBreakpointId);
438 }
439 m_breakpointIdToDebuggerBreakpointIds.remove(debuggerBreakpointIdsIterator);
440 }
441
442 void V8DebuggerAgent::continueToLocation(ErrorString* errorString, const RefPtr< JSONObject>& location, const bool* interstateLocationOpt)
443 {
444 if (!checkEnabled(errorString))
445 return;
446 if (!m_continueToLocationBreakpointId.isEmpty()) {
447 debugger().removeBreakpoint(m_continueToLocationBreakpointId);
448 m_continueToLocationBreakpointId = "";
449 }
450
451 String scriptId;
452 int lineNumber;
453 int columnNumber;
454
455 if (!parseLocation(errorString, location, &scriptId, &lineNumber, &columnNum ber))
456 return;
457
458 ScriptBreakpoint breakpoint(lineNumber, columnNumber, "");
459 m_continueToLocationBreakpointId = debugger().setBreakpoint(scriptId, breakp oint, &lineNumber, &columnNumber, asBool(interstateLocationOpt));
460 resume(errorString);
461 }
462
463 void V8DebuggerAgent::getStepInPositions(ErrorString* errorString, const String& callFrameId, RefPtr<Array<TypeBuilder::Debugger::Location>>& positions)
464 {
465 if (!isPaused() || m_currentCallStack.IsEmpty()) {
466 *errorString = "Attempt to access callframe when debugger is not on paus e";
467 return;
468 }
469 OwnPtr<RemoteCallFrameId> remoteId = RemoteCallFrameId::parse(callFrameId);
470 if (!remoteId) {
471 *errorString = "Invalid call frame id";
472 return;
473 }
474 InjectedScript injectedScript = m_injectedScriptManager->findInjectedScript( remoteId.get());
475 if (injectedScript.isEmpty()) {
476 *errorString = "Inspected frame has gone";
477 return;
478 }
479
480 v8::HandleScope scope(m_isolate);
481 v8::Local<v8::Object> callStack = m_currentCallStack.Get(m_isolate);
482 injectedScript.getStepInPositions(errorString, callStack, callFrameId, posit ions);
483 }
484
485 void V8DebuggerAgent::getBacktrace(ErrorString* errorString, RefPtr<Array<CallFr ame>>& callFrames, RefPtr<StackTrace>& asyncStackTrace)
486 {
487 if (!assertPaused(errorString))
488 return;
489 m_currentCallStack.Reset(m_isolate, debugger().currentCallFrames());
490 callFrames = currentCallFrames();
491 asyncStackTrace = currentAsyncStackTrace();
492 }
493
494 bool V8DebuggerAgent::isCallStackEmptyOrBlackboxed()
495 {
496 ASSERT(enabled());
497 for (int index = 0; ; ++index) {
498 RefPtr<JavaScriptCallFrame> frame = debugger().callFrameNoScopes(index);
499 if (!frame)
500 break;
501 if (!isCallFrameWithUnknownScriptOrBlackboxed(frame.release()))
502 return false;
503 }
504 return true;
505 }
506
507 bool V8DebuggerAgent::isTopCallFrameBlackboxed()
508 {
509 ASSERT(enabled());
510 return isCallFrameWithUnknownScriptOrBlackboxed(debugger().callFrameNoScopes (0));
511 }
512
513 bool V8DebuggerAgent::isCallFrameWithUnknownScriptOrBlackboxed(PassRefPtr<JavaSc riptCallFrame> pFrame)
514 {
515 RefPtr<JavaScriptCallFrame> frame = pFrame;
516 if (!frame)
517 return true;
518 ScriptsMap::iterator it = m_scripts.find(String::number(frame->sourceID()));
519 if (it == m_scripts.end()) {
520 // Unknown scripts are blackboxed.
521 return true;
522 }
523 if (m_skipContentScripts && it->value.isContentScript())
524 return true;
525 bool isBlackboxed = false;
526 String scriptURL = it->value.sourceURL();
527 if (m_cachedSkipStackRegExp && !scriptURL.isEmpty()) {
528 if (!it->value.getBlackboxedState(m_cachedSkipStackGeneration, &isBlackb oxed)) {
529 isBlackboxed = m_cachedSkipStackRegExp->match(scriptURL) != -1;
530 it->value.setBlackboxedState(m_cachedSkipStackGeneration, isBlackbox ed);
531 }
532 }
533 return isBlackboxed;
534 }
535
536 V8DebuggerListener::SkipPauseRequest V8DebuggerAgent::shouldSkipExceptionPause()
537 {
538 if (m_steppingFromFramework)
539 return V8DebuggerListener::NoSkip;
540 if (isTopCallFrameBlackboxed())
541 return V8DebuggerListener::Continue;
542 return V8DebuggerListener::NoSkip;
543 }
544
545 V8DebuggerListener::SkipPauseRequest V8DebuggerAgent::shouldSkipStepPause()
546 {
547 if (m_steppingFromFramework)
548 return V8DebuggerListener::NoSkip;
549
550 if (m_skipNextDebuggerStepOut) {
551 m_skipNextDebuggerStepOut = false;
552 if (m_scheduledDebuggerStep == StepOut)
553 return V8DebuggerListener::StepOut;
554 }
555
556 if (!isTopCallFrameBlackboxed())
557 return V8DebuggerListener::NoSkip;
558
559 if (m_skippedStepFrameCount >= maxSkipStepFrameCount)
560 return V8DebuggerListener::StepOut;
561
562 if (!m_skippedStepFrameCount)
563 m_recursionLevelForStepFrame = 1;
564
565 ++m_skippedStepFrameCount;
566 return V8DebuggerListener::StepFrame;
567 }
568
569 PassRefPtr<TypeBuilder::Debugger::Location> V8DebuggerAgent::resolveBreakpoint(c onst String& breakpointId, const String& scriptId, const ScriptBreakpoint& break point, BreakpointSource source)
570 {
571 ASSERT(enabled());
572 // FIXME: remove these checks once crbug.com/520702 is resolved.
573 RELEASE_ASSERT(!breakpointId.isEmpty());
574 RELEASE_ASSERT(!scriptId.isEmpty());
575 ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId);
576 if (scriptIterator == m_scripts.end())
577 return nullptr;
578 Script& script = scriptIterator->value;
579 if (breakpoint.lineNumber < script.startLine() || script.endLine() < breakpo int.lineNumber)
580 return nullptr;
581
582 int actualLineNumber;
583 int actualColumnNumber;
584 String debuggerBreakpointId = debugger().setBreakpoint(scriptId, breakpoint, &actualLineNumber, &actualColumnNumber, false);
585 if (debuggerBreakpointId.isEmpty())
586 return nullptr;
587
588 m_serverBreakpoints.set(debuggerBreakpointId, std::make_pair(breakpointId, s ource));
589
590 RELEASE_ASSERT(!breakpointId.isEmpty());
591 BreakpointIdToDebuggerBreakpointIdsMap::iterator debuggerBreakpointIdsIterat or = m_breakpointIdToDebuggerBreakpointIds.find(breakpointId);
592 if (debuggerBreakpointIdsIterator == m_breakpointIdToDebuggerBreakpointIds.e nd())
593 m_breakpointIdToDebuggerBreakpointIds.set(breakpointId, Vector<String>() ).storedValue->value.append(debuggerBreakpointId);
594 else
595 debuggerBreakpointIdsIterator->value.append(debuggerBreakpointId);
596
597 RefPtr<TypeBuilder::Debugger::Location> location = TypeBuilder::Debugger::Lo cation::create()
598 .setScriptId(scriptId)
599 .setLineNumber(actualLineNumber);
600 location->setColumnNumber(actualColumnNumber);
601 return location;
602 }
603
604 void V8DebuggerAgent::searchInContent(ErrorString* error, const String& scriptId , const String& query, const bool* const optionalCaseSensitive, const bool* cons t optionalIsRegex, RefPtr<Array<TypeBuilder::Debugger::SearchMatch>>& results)
605 {
606 ScriptsMap::iterator it = m_scripts.find(scriptId);
607 if (it != m_scripts.end())
608 results = ContentSearchUtils::searchInTextByLines(it->value.source(), qu ery, asBool(optionalCaseSensitive), asBool(optionalIsRegex));
609 else
610 *error = "No script for id: " + scriptId;
611 }
612
613 void V8DebuggerAgent::setScriptSource(ErrorString* error, RefPtr<TypeBuilder::De bugger::SetScriptSourceError>& errorData, const String& scriptId, const String& newContent, const bool* const preview, RefPtr<Array<CallFrame>>& newCallFrames, TypeBuilder::OptOutput<bool>* stackChanged, RefPtr<StackTrace>& asyncStackTrace)
614 {
615 if (!checkEnabled(error))
616 return;
617 if (!debugger().setScriptSource(scriptId, newContent, asBool(preview), error , errorData, &m_currentCallStack, stackChanged))
618 return;
619
620 newCallFrames = currentCallFrames();
621 asyncStackTrace = currentAsyncStackTrace();
622
623 ScriptsMap::iterator it = m_scripts.find(scriptId);
624 if (it == m_scripts.end())
625 return;
626 it->value.setSource(newContent);
627 }
628
629 void V8DebuggerAgent::restartFrame(ErrorString* errorString, const String& callF rameId, RefPtr<Array<CallFrame>>& newCallFrames, RefPtr<StackTrace>& asyncStackT race)
630 {
631 if (!isPaused() || m_currentCallStack.IsEmpty()) {
632 *errorString = "Attempt to access callframe when debugger is not on paus e";
633 return;
634 }
635 OwnPtr<RemoteCallFrameId> remoteId = RemoteCallFrameId::parse(callFrameId);
636 if (!remoteId) {
637 *errorString = "Invalid call frame id";
638 return;
639 }
640 InjectedScript injectedScript = m_injectedScriptManager->findInjectedScript( remoteId.get());
641 if (injectedScript.isEmpty()) {
642 *errorString = "Inspected frame has gone";
643 return;
644 }
645
646 v8::HandleScope scope(m_isolate);
647 v8::Local<v8::Object> callStack = m_currentCallStack.Get(m_isolate);
648 injectedScript.restartFrame(errorString, callStack, callFrameId);
649 m_currentCallStack.Reset(m_isolate, debugger().currentCallFrames());
650 newCallFrames = currentCallFrames();
651 asyncStackTrace = currentAsyncStackTrace();
652 }
653
654 void V8DebuggerAgent::getScriptSource(ErrorString* error, const String& scriptId , String* scriptSource)
655 {
656 if (!checkEnabled(error))
657 return;
658 ScriptsMap::iterator it = m_scripts.find(scriptId);
659 if (it == m_scripts.end()) {
660 *error = "No script for id: " + scriptId;
661 return;
662 }
663 *scriptSource = it->value.source();
664 }
665
666 void V8DebuggerAgent::getFunctionDetails(ErrorString* errorString, const String& functionId, RefPtr<FunctionDetails>& details)
667 {
668 if (!checkEnabled(errorString))
669 return;
670 OwnPtr<RemoteObjectId> remoteId = RemoteObjectId::parse(functionId);
671 if (!remoteId) {
672 *errorString = "Invalid object id";
673 return;
674 }
675 InjectedScript injectedScript = m_injectedScriptManager->findInjectedScript( remoteId.get());
676 if (injectedScript.isEmpty()) {
677 *errorString = "Function object id is obsolete";
678 return;
679 }
680 injectedScript.getFunctionDetails(errorString, functionId, &details);
681 }
682
683 void V8DebuggerAgent::getGeneratorObjectDetails(ErrorString* errorString, const String& objectId, RefPtr<GeneratorObjectDetails>& details)
684 {
685 if (!checkEnabled(errorString))
686 return;
687 OwnPtr<RemoteObjectId> remoteId = RemoteObjectId::parse(objectId);
688 if (!remoteId) {
689 *errorString = "Invalid object id";
690 return;
691 }
692 InjectedScript injectedScript = m_injectedScriptManager->findInjectedScript( remoteId.get());
693 if (injectedScript.isEmpty()) {
694 *errorString = "Inspected frame has gone";
695 return;
696 }
697 injectedScript.getGeneratorObjectDetails(errorString, objectId, &details);
698 }
699
700 void V8DebuggerAgent::getCollectionEntries(ErrorString* errorString, const Strin g& objectId, RefPtr<TypeBuilder::Array<CollectionEntry>>& entries)
701 {
702 if (!checkEnabled(errorString))
703 return;
704 OwnPtr<RemoteObjectId> remoteId = RemoteObjectId::parse(objectId);
705 if (!remoteId) {
706 *errorString = "Invalid object id";
707 return;
708 }
709 InjectedScript injectedScript = m_injectedScriptManager->findInjectedScript( remoteId.get());
710 if (injectedScript.isEmpty()) {
711 *errorString = "Inspected frame has gone";
712 return;
713 }
714 injectedScript.getCollectionEntries(errorString, objectId, &entries);
715 }
716
717 void V8DebuggerAgent::schedulePauseOnNextStatement(InspectorFrontend::Debugger:: Reason::Enum breakReason, PassRefPtr<JSONObject> data)
718 {
719 ASSERT(enabled());
720 if (m_scheduledDebuggerStep == StepInto || m_javaScriptPauseScheduled || isP aused())
721 return;
722 m_breakReason = breakReason;
723 m_breakAuxData = data;
724 m_pausingOnNativeEvent = true;
725 m_skipNextDebuggerStepOut = false;
726 debugger().setPauseOnNextStatement(true);
727 }
728
729 void V8DebuggerAgent::schedulePauseOnNextStatementIfSteppingInto()
730 {
731 ASSERT(enabled());
732 if (m_scheduledDebuggerStep != StepInto || m_javaScriptPauseScheduled || isP aused())
733 return;
734 clearBreakDetails();
735 m_pausingOnNativeEvent = false;
736 m_skippedStepFrameCount = 0;
737 m_recursionLevelForStepFrame = 0;
738 debugger().setPauseOnNextStatement(true);
739 }
740
741 void V8DebuggerAgent::cancelPauseOnNextStatement()
742 {
743 if (m_javaScriptPauseScheduled || isPaused())
744 return;
745 clearBreakDetails();
746 m_pausingOnNativeEvent = false;
747 debugger().setPauseOnNextStatement(false);
748 }
749
750 bool V8DebuggerAgent::v8AsyncTaskEventsEnabled() const
751 {
752 return trackingAsyncCalls();
753 }
754
755 void V8DebuggerAgent::didReceiveV8AsyncTaskEvent(v8::Local<v8::Context> context, const String& eventType, const String& eventName, int id)
756 {
757 ASSERT(trackingAsyncCalls());
758 ScriptState* state = ScriptState::from(context);
759 m_v8AsyncCallTracker->didReceiveV8AsyncTaskEvent(state, eventType, eventName , id);
760 }
761
762 bool V8DebuggerAgent::v8PromiseEventsEnabled() const
763 {
764 return m_promiseTracker->isEnabled();
765 }
766
767 void V8DebuggerAgent::didReceiveV8PromiseEvent(v8::Local<v8::Context> context, v 8::Local<v8::Object> promise, v8::Local<v8::Value> parentPromise, int status)
768 {
769 ASSERT(m_promiseTracker->isEnabled());
770 ScriptState* scriptState = ScriptState::from(context);
771 m_promiseTracker->didReceiveV8PromiseEvent(scriptState, promise, parentPromi se, status);
772 }
773
774 void V8DebuggerAgent::pause(ErrorString* errorString)
775 {
776 if (!checkEnabled(errorString))
777 return;
778 if (m_javaScriptPauseScheduled || isPaused())
779 return;
780 clearBreakDetails();
781 clearStepIntoAsync();
782 m_javaScriptPauseScheduled = true;
783 m_scheduledDebuggerStep = NoStep;
784 m_skippedStepFrameCount = 0;
785 m_steppingFromFramework = false;
786 debugger().setPauseOnNextStatement(true);
787 }
788
789 void V8DebuggerAgent::resume(ErrorString* errorString)
790 {
791 if (!assertPaused(errorString))
792 return;
793 m_scheduledDebuggerStep = NoStep;
794 m_steppingFromFramework = false;
795 m_injectedScriptManager->releaseObjectGroup(V8DebuggerAgent::backtraceObject Group);
796 debugger().continueProgram();
797 }
798
799 void V8DebuggerAgent::stepOver(ErrorString* errorString)
800 {
801 if (!assertPaused(errorString))
802 return;
803 // StepOver at function return point should fallback to StepInto.
804 RefPtr<JavaScriptCallFrame> frame = debugger().callFrameNoScopes(0);
805 if (frame && frame->isAtReturn()) {
806 stepInto(errorString);
807 return;
808 }
809 m_scheduledDebuggerStep = StepOver;
810 m_steppingFromFramework = isTopCallFrameBlackboxed();
811 m_injectedScriptManager->releaseObjectGroup(V8DebuggerAgent::backtraceObject Group);
812 debugger().stepOverStatement();
813 }
814
815 void V8DebuggerAgent::stepInto(ErrorString* errorString)
816 {
817 if (!assertPaused(errorString))
818 return;
819 m_scheduledDebuggerStep = StepInto;
820 m_steppingFromFramework = isTopCallFrameBlackboxed();
821 m_injectedScriptManager->releaseObjectGroup(V8DebuggerAgent::backtraceObject Group);
822 debugger().stepIntoStatement();
823 }
824
825 void V8DebuggerAgent::stepOut(ErrorString* errorString)
826 {
827 if (!assertPaused(errorString))
828 return;
829 m_scheduledDebuggerStep = StepOut;
830 m_skipNextDebuggerStepOut = false;
831 m_recursionLevelForStepOut = 1;
832 m_steppingFromFramework = isTopCallFrameBlackboxed();
833 m_injectedScriptManager->releaseObjectGroup(V8DebuggerAgent::backtraceObject Group);
834 debugger().stepOutOfFunction();
835 }
836
837 void V8DebuggerAgent::stepIntoAsync(ErrorString* errorString)
838 {
839 if (!assertPaused(errorString))
840 return;
841 if (!trackingAsyncCalls()) {
842 *errorString = "Can only perform operation if async call stacks are enab led.";
843 return;
844 }
845 clearStepIntoAsync();
846 m_startingStepIntoAsync = true;
847 stepInto(errorString);
848 }
849
850 void V8DebuggerAgent::setPauseOnExceptions(ErrorString* errorString, const Strin g& stringPauseState)
851 {
852 if (!checkEnabled(errorString))
853 return;
854 V8Debugger::PauseOnExceptionsState pauseState;
855 if (stringPauseState == "none") {
856 pauseState = V8Debugger::DontPauseOnExceptions;
857 } else if (stringPauseState == "all") {
858 pauseState = V8Debugger::PauseOnAllExceptions;
859 } else if (stringPauseState == "uncaught") {
860 pauseState = V8Debugger::PauseOnUncaughtExceptions;
861 } else {
862 *errorString = "Unknown pause on exceptions mode: " + stringPauseState;
863 return;
864 }
865 setPauseOnExceptionsImpl(errorString, pauseState);
866 }
867
868 void V8DebuggerAgent::setPauseOnExceptionsImpl(ErrorString* errorString, int pau seState)
869 {
870 debugger().setPauseOnExceptionsState(static_cast<V8Debugger::PauseOnExceptio nsState>(pauseState));
871 if (debugger().pauseOnExceptionsState() != pauseState)
872 *errorString = "Internal error. Could not change pause on exceptions sta te";
873 else
874 m_state->setLong(DebuggerAgentState::pauseOnExceptionsState, pauseState) ;
875 }
876
877 bool V8DebuggerAgent::callStackForId(ErrorString* errorString, const RemoteCallF rameId& callFrameId, v8::Local<v8::Object>* callStack, bool* isAsync)
878 {
879 unsigned asyncOrdinal = callFrameId.asyncStackOrdinal(); // 0 is current cal l stack
880 if (!asyncOrdinal) {
881 *callStack = m_currentCallStack.Get(m_isolate);
882 *isAsync = false;
883 return true;
884 }
885 if (!m_currentAsyncCallChain || asyncOrdinal < 1 || asyncOrdinal >= m_curren tAsyncCallChain->callStacks().size()) {
886 *errorString = "Async call stack not found";
887 return false;
888 }
889 RefPtrWillBeRawPtr<AsyncCallStack> asyncStack = m_currentAsyncCallChain->cal lStacks()[asyncOrdinal - 1];
890 *callStack = asyncStack->callFrames(m_isolate);
891 *isAsync = true;
892 return true;
893 }
894
895 void V8DebuggerAgent::evaluateOnCallFrame(ErrorString* errorString, const String & callFrameId, const String& expression, const String* const objectGroup, const bool* const includeCommandLineAPI, const bool* const doNotPauseOnExceptionsAndMu teConsole, const bool* const returnByValue, const bool* generatePreview, RefPtr< RemoteObject>& result, TypeBuilder::OptOutput<bool>* wasThrown, RefPtr<TypeBuild er::Debugger::ExceptionDetails>& exceptionDetails)
896 {
897 if (!isPaused() || m_currentCallStack.IsEmpty()) {
898 *errorString = "Attempt to access callframe when debugger is not on paus e";
899 return;
900 }
901 OwnPtr<RemoteCallFrameId> remoteId = RemoteCallFrameId::parse(callFrameId);
902 if (!remoteId) {
903 *errorString = "Invalid call frame id";
904 return;
905 }
906 InjectedScript injectedScript = m_injectedScriptManager->findInjectedScript( remoteId.get());
907 if (injectedScript.isEmpty()) {
908 *errorString = "Inspected frame has gone";
909 return;
910 }
911
912 v8::HandleScope scope(m_isolate);
913 bool isAsync = false;
914 v8::Local<v8::Object> callStack;
915 if (!callStackForId(errorString, *remoteId, &callStack, &isAsync))
916 return;
917 ASSERT(!callStack.IsEmpty());
918
919 V8Debugger::PauseOnExceptionsState previousPauseOnExceptionsState = debugger ().pauseOnExceptionsState();
920 if (asBool(doNotPauseOnExceptionsAndMuteConsole)) {
921 if (previousPauseOnExceptionsState != V8Debugger::DontPauseOnExceptions)
922 debugger().setPauseOnExceptionsState(V8Debugger::DontPauseOnExceptio ns);
923 m_client->muteConsole();
924 }
925
926 injectedScript.evaluateOnCallFrame(errorString, callStack, isAsync, callFram eId, expression, objectGroup ? *objectGroup : "", asBool(includeCommandLineAPI), asBool(returnByValue), asBool(generatePreview), &result, wasThrown, &exceptionD etails);
927 if (asBool(doNotPauseOnExceptionsAndMuteConsole)) {
928 m_client->unmuteConsole();
929 if (debugger().pauseOnExceptionsState() != previousPauseOnExceptionsStat e)
930 debugger().setPauseOnExceptionsState(previousPauseOnExceptionsState) ;
931 }
932 }
933
934 InjectedScript V8DebuggerAgent::injectedScriptForEval(ErrorString* errorString, const int* executionContextId)
935 {
936 InjectedScript injectedScript = executionContextId ? m_injectedScriptManager ->injectedScriptForId(*executionContextId) : m_client->defaultInjectedScript();
937 if (injectedScript.isEmpty())
938 *errorString = "Execution context with given id not found.";
939 return injectedScript;
940 }
941
942 void V8DebuggerAgent::compileScript(ErrorString* errorString, const String& expr ession, const String& sourceURL, bool persistScript, const int* executionContext Id, TypeBuilder::OptOutput<ScriptId>* scriptId, RefPtr<ExceptionDetails>& except ionDetails)
943 {
944 if (!checkEnabled(errorString))
945 return;
946 InjectedScript injectedScript = injectedScriptForEval(errorString, execution ContextId);
947 if (injectedScript.isEmpty() || !injectedScript.scriptState()->contextIsVali d()) {
948 *errorString = "Inspected frame has gone";
949 return;
950 }
951
952 ScriptState::Scope scope(injectedScript.scriptState());
953 v8::Local<v8::String> source = v8String(m_isolate, expression);
954 v8::TryCatch tryCatch;
955 v8::Local<v8::Script> script;
956 if (!v8Call(V8ScriptRunner::compileScript(source, sourceURL, String(), TextP osition(), m_isolate), script, tryCatch)) {
957 v8::Local<v8::Message> message = tryCatch.Message();
958 if (!message.IsEmpty())
959 exceptionDetails = createExceptionDetails(m_isolate, message);
960 else
961 *errorString = "Script compilation failed";
962 return;
963 }
964
965 if (!persistScript)
966 return;
967
968 String scriptValueId = String::number(script->GetUnboundScript()->GetId());
969 m_compiledScripts.Set(scriptValueId, script);
970 *scriptId = scriptValueId;
971 }
972
973 void V8DebuggerAgent::runScript(ErrorString* errorString, const ScriptId& script Id, const int* executionContextId, const String* const objectGroup, const bool* const doNotPauseOnExceptionsAndMuteConsole, RefPtr<RemoteObject>& result, RefPtr <ExceptionDetails>& exceptionDetails)
974 {
975 if (!checkEnabled(errorString))
976 return;
977 InjectedScript injectedScript = injectedScriptForEval(errorString, execution ContextId);
978 if (injectedScript.isEmpty()) {
979 *errorString = "Inspected frame has gone";
980 return;
981 }
982
983 V8Debugger::PauseOnExceptionsState previousPauseOnExceptionsState = debugger ().pauseOnExceptionsState();
984 if (asBool(doNotPauseOnExceptionsAndMuteConsole)) {
985 if (previousPauseOnExceptionsState != V8Debugger::DontPauseOnExceptions)
986 debugger().setPauseOnExceptionsState(V8Debugger::DontPauseOnExceptio ns);
987 m_client->muteConsole();
988 }
989
990 if (!m_compiledScripts.Contains(scriptId)) {
991 *errorString = "Script execution failed";
992 return;
993 }
994
995 ScriptState* scriptState = injectedScript.scriptState();
996 ScriptState::Scope scope(scriptState);
997 v8::Local<v8::Script> script = v8::Local<v8::Script>::New(m_isolate, m_compi ledScripts.Remove(scriptId));
998
999 if (script.IsEmpty() || !scriptState->contextIsValid()) {
1000 *errorString = "Script execution failed";
1001 return;
1002 }
1003 v8::TryCatch tryCatch;
1004 v8::Local<v8::Value> value;
1005 ScriptValue scriptValue;
1006 if (v8Call(V8ScriptRunner::runCompiledScript(m_isolate, script, scriptState- >executionContext()), value, tryCatch)) {
1007 scriptValue = ScriptValue(scriptState, value);
1008 } else {
1009 scriptValue = ScriptValue(scriptState, tryCatch.Exception());
1010 v8::Local<v8::Message> message = tryCatch.Message();
1011 if (!message.IsEmpty())
1012 exceptionDetails = createExceptionDetails(m_isolate, message);
1013 }
1014
1015 if (scriptValue.isEmpty()) {
1016 *errorString = "Script execution failed";
1017 return;
1018 }
1019
1020 result = injectedScript.wrapObject(scriptValue, objectGroup ? *objectGroup : "");
1021
1022 if (asBool(doNotPauseOnExceptionsAndMuteConsole)) {
1023 m_client->unmuteConsole();
1024 if (debugger().pauseOnExceptionsState() != previousPauseOnExceptionsStat e)
1025 debugger().setPauseOnExceptionsState(previousPauseOnExceptionsState) ;
1026 }
1027 }
1028
1029 void V8DebuggerAgent::setVariableValue(ErrorString* errorString, int scopeNumber , const String& variableName, const RefPtr<JSONObject>& newValue, const String* callFrameId, const String* functionObjectId)
1030 {
1031 if (!checkEnabled(errorString))
1032 return;
1033 InjectedScript injectedScript;
1034 if (callFrameId) {
1035 if (!isPaused() || m_currentCallStack.IsEmpty()) {
1036 *errorString = "Attempt to access callframe when debugger is not on pause";
1037 return;
1038 }
1039 OwnPtr<RemoteCallFrameId> remoteId = RemoteCallFrameId::parse(*callFrame Id);
1040 if (!remoteId) {
1041 *errorString = "Invalid call frame id";
1042 return;
1043 }
1044 injectedScript = m_injectedScriptManager->findInjectedScript(remoteId.ge t());
1045 if (injectedScript.isEmpty()) {
1046 *errorString = "Inspected frame has gone";
1047 return;
1048 }
1049 } else if (functionObjectId) {
1050 OwnPtr<RemoteObjectId> remoteId = RemoteObjectId::parse(*functionObjectI d);
1051 if (!remoteId) {
1052 *errorString = "Invalid object id";
1053 return;
1054 }
1055 injectedScript = m_injectedScriptManager->findInjectedScript(remoteId.ge t());
1056 if (injectedScript.isEmpty()) {
1057 *errorString = "Function object id cannot be resolved";
1058 return;
1059 }
1060 } else {
1061 *errorString = "Either call frame or function object must be specified";
1062 return;
1063 }
1064 String newValueString = newValue->toJSONString();
1065
1066 v8::HandleScope scope(m_isolate);
1067 v8::Local<v8::Object> currentCallStack = m_currentCallStack.Get(m_isolate);
1068 injectedScript.setVariableValue(errorString, currentCallStack, callFrameId, functionObjectId, scopeNumber, variableName, newValueString);
1069 }
1070
1071 void V8DebuggerAgent::skipStackFrames(ErrorString* errorString, const String* pa ttern, const bool* skipContentScripts)
1072 {
1073 if (!checkEnabled(errorString))
1074 return;
1075 OwnPtr<ScriptRegexp> compiled;
1076 String patternValue = pattern ? *pattern : "";
1077 if (!patternValue.isEmpty()) {
1078 compiled = compileSkipCallFramePattern(patternValue);
1079 if (!compiled) {
1080 *errorString = "Invalid regular expression";
1081 return;
1082 }
1083 }
1084 m_state->setString(DebuggerAgentState::skipStackPattern, patternValue);
1085 m_cachedSkipStackRegExp = compiled.release();
1086 increaseCachedSkipStackGeneration();
1087 m_skipContentScripts = asBool(skipContentScripts);
1088 m_state->setBoolean(DebuggerAgentState::skipContentScripts, m_skipContentScr ipts);
1089 }
1090
1091 void V8DebuggerAgent::setAsyncCallStackDepth(ErrorString* errorString, int depth )
1092 {
1093 if (!checkEnabled(errorString))
1094 return;
1095 m_state->setLong(DebuggerAgentState::asyncCallStackDepth, depth);
1096 internalSetAsyncCallStackDepth(depth);
1097 }
1098
1099 void V8DebuggerAgent::enablePromiseTracker(ErrorString* errorString, const bool* captureStacks)
1100 {
1101 if (!checkEnabled(errorString))
1102 return;
1103 m_state->setBoolean(DebuggerAgentState::promiseTrackerEnabled, true);
1104 m_state->setBoolean(DebuggerAgentState::promiseTrackerCaptureStacks, asBool( captureStacks));
1105 m_promiseTracker->setEnabled(true, asBool(captureStacks));
1106 }
1107
1108 void V8DebuggerAgent::disablePromiseTracker(ErrorString* errorString)
1109 {
1110 if (!checkEnabled(errorString))
1111 return;
1112 m_state->setBoolean(DebuggerAgentState::promiseTrackerEnabled, false);
1113 m_promiseTracker->setEnabled(false, false);
1114 }
1115
1116 void V8DebuggerAgent::getPromiseById(ErrorString* errorString, int promiseId, co nst String* objectGroup, RefPtr<RemoteObject>& promise)
1117 {
1118 if (!checkEnabled(errorString))
1119 return;
1120 if (!m_promiseTracker->isEnabled()) {
1121 *errorString = "Promise tracking is disabled";
1122 return;
1123 }
1124 ScriptValue value = m_promiseTracker->promiseById(promiseId);
1125 if (value.isEmpty()) {
1126 *errorString = "Promise with specified ID not found.";
1127 return;
1128 }
1129 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(v alue.scriptState());
1130 promise = injectedScript.wrapObject(value, objectGroup ? *objectGroup : "");
1131 }
1132
1133 void V8DebuggerAgent::didUpdatePromise(InspectorFrontend::Debugger::EventType::E num eventType, PassRefPtr<TypeBuilder::Debugger::PromiseDetails> promise)
1134 {
1135 if (m_frontend)
1136 m_frontend->promiseUpdated(eventType, promise);
1137 }
1138
1139 int V8DebuggerAgent::traceAsyncOperationStarting(const String& description)
1140 {
1141 v8::HandleScope scope(m_isolate);
1142 v8::Local<v8::Object> callFrames = debugger().currentCallFramesForAsyncStack ();
1143 RefPtrWillBeRawPtr<AsyncCallChain> chain = nullptr;
1144 if (callFrames.IsEmpty()) {
1145 if (m_currentAsyncCallChain)
1146 chain = AsyncCallChain::create(nullptr, m_currentAsyncCallChain.get( ), m_maxAsyncCallStackDepth);
1147 } else {
1148 chain = AsyncCallChain::create(adoptRefWillBeNoop(new AsyncCallStack(des cription, callFrames)), m_currentAsyncCallChain.get(), m_maxAsyncCallStackDepth) ;
1149 }
1150 do {
1151 ++m_lastAsyncOperationId;
1152 if (m_lastAsyncOperationId <= 0)
1153 m_lastAsyncOperationId = 1;
1154 } while (m_asyncOperations.contains(m_lastAsyncOperationId));
1155 m_asyncOperations.set(m_lastAsyncOperationId, chain);
1156 if (chain)
1157 m_asyncOperationNotifications.add(m_lastAsyncOperationId);
1158
1159 if (m_startingStepIntoAsync) {
1160 // We have successfully started a StepIntoAsync, so revoke the debugger' s StepInto
1161 // and wait for the corresponding async operation breakpoint.
1162 ASSERT(m_pausingAsyncOperations.isEmpty());
1163 m_pausingAsyncOperations.add(m_lastAsyncOperationId);
1164 m_startingStepIntoAsync = false;
1165 m_scheduledDebuggerStep = NoStep;
1166 debugger().clearStepping();
1167 } else if (m_pausingOnAsyncOperation) {
1168 m_pausingAsyncOperations.add(m_lastAsyncOperationId);
1169 }
1170
1171 if (m_pausedScriptState)
1172 flushAsyncOperationEvents(nullptr);
1173 return m_lastAsyncOperationId;
1174 }
1175
1176 void V8DebuggerAgent::traceAsyncCallbackStarting(int operationId)
1177 {
1178 ASSERT(operationId > 0 || operationId == unknownAsyncOperationId);
1179 AsyncCallChain* chain = operationId > 0 ? m_asyncOperations.get(operationId) : nullptr;
1180 // FIXME: extract recursion check into a delegate.
1181 int recursionLevel = V8RecursionScope::recursionLevel(m_isolate);
1182 if (chain && (!recursionLevel || (recursionLevel == 1 && Microtask::performi ngCheckpoint(m_isolate)))) {
1183 // There can be still an old m_currentAsyncCallChain set if we start run ning Microtasks
1184 // right after executing a JS callback but before the corresponding trac eAsyncCallbackCompleted().
1185 // In this case just call traceAsyncCallbackCompleted() now, and the sub sequent ones will be ignored.
1186 //
1187 // The nested levels count may be greater than 1, for example, when even ts are guarded via custom
1188 // traceAsync* calls, like in window.postMessage(). In this case there w ill be a willHandleEvent
1189 // instrumentation with unknownAsyncOperationId bumping up the nested le vels count.
1190 if (m_currentAsyncCallChain) {
1191 ASSERT(m_nestedAsyncCallCount >= 1);
1192 ASSERT(recursionLevel == 1 && Microtask::performingCheckpoint(m_isol ate));
1193 m_nestedAsyncCallCount = 1;
1194 traceAsyncCallbackCompleted();
1195 }
1196
1197 // Current AsyncCallChain corresponds to the bottommost JS call frame.
1198 ASSERT(!m_currentAsyncCallChain);
1199 m_currentAsyncCallChain = chain;
1200 m_currentAsyncOperationId = operationId;
1201 m_pendingTraceAsyncOperationCompleted = false;
1202 m_nestedAsyncCallCount = 1;
1203
1204 if (m_pausingAsyncOperations.contains(operationId) || m_asyncOperationBr eakpoints.contains(operationId)) {
1205 m_pausingOnAsyncOperation = true;
1206 m_scheduledDebuggerStep = StepInto;
1207 m_skippedStepFrameCount = 0;
1208 m_recursionLevelForStepFrame = 0;
1209 debugger().setPauseOnNextStatement(true);
1210 }
1211 } else {
1212 if (m_currentAsyncCallChain)
1213 ++m_nestedAsyncCallCount;
1214 }
1215 }
1216
1217 void V8DebuggerAgent::traceAsyncCallbackCompleted()
1218 {
1219 if (!m_nestedAsyncCallCount)
1220 return;
1221 ASSERT(m_currentAsyncCallChain);
1222 --m_nestedAsyncCallCount;
1223 if (!m_nestedAsyncCallCount) {
1224 clearCurrentAsyncOperation();
1225 if (!m_pausingOnAsyncOperation)
1226 return;
1227 m_pausingOnAsyncOperation = false;
1228 m_scheduledDebuggerStep = NoStep;
1229 debugger().setPauseOnNextStatement(false);
1230 if (m_startingStepIntoAsync && m_pausingAsyncOperations.isEmpty())
1231 clearStepIntoAsync();
1232 }
1233 }
1234
1235 void V8DebuggerAgent::traceAsyncOperationCompleted(int operationId)
1236 {
1237 ASSERT(operationId > 0 || operationId == unknownAsyncOperationId);
1238 bool shouldNotify = false;
1239 if (operationId > 0) {
1240 if (m_currentAsyncOperationId == operationId) {
1241 if (m_pendingTraceAsyncOperationCompleted) {
1242 m_pendingTraceAsyncOperationCompleted = false;
1243 } else {
1244 // Delay traceAsyncOperationCompleted() until the last async cal lback (being currently executed) is done.
1245 m_pendingTraceAsyncOperationCompleted = true;
1246 return;
1247 }
1248 }
1249 m_asyncOperations.remove(operationId);
1250 m_asyncOperationBreakpoints.remove(operationId);
1251 m_pausingAsyncOperations.remove(operationId);
1252 shouldNotify = !m_asyncOperationNotifications.take(operationId);
1253 }
1254 if (m_startingStepIntoAsync) {
1255 if (!m_pausingOnAsyncOperation && m_pausingAsyncOperations.isEmpty())
1256 clearStepIntoAsync();
1257 }
1258 if (m_frontend && shouldNotify)
1259 m_frontend->asyncOperationCompleted(operationId);
1260 }
1261
1262 void V8DebuggerAgent::flushAsyncOperationEvents(ErrorString*)
1263 {
1264 if (!m_frontend)
1265 return;
1266
1267 for (int operationId : m_asyncOperationNotifications) {
1268 RefPtrWillBeRawPtr<AsyncCallChain> chain = m_asyncOperations.get(operati onId);
1269 ASSERT(chain);
1270 const AsyncCallStackVector& callStacks = chain->callStacks();
1271 ASSERT(!callStacks.isEmpty());
1272
1273 RefPtr<AsyncOperation> operation;
1274 RefPtr<AsyncStackTrace> lastAsyncStackTrace;
1275 for (const auto& callStack : callStacks) {
1276 v8::HandleScope scope(m_isolate);
1277 RefPtrWillBeRawPtr<ScriptCallStack> scriptCallStack = toScriptCallSt ack(callStack->callFrames(m_isolate));
1278 if (!scriptCallStack)
1279 break;
1280 if (!operation) {
1281 operation = AsyncOperation::create()
1282 .setId(operationId)
1283 .setDescription(callStack->description())
1284 .release();
1285 operation->setStackTrace(scriptCallStack->buildInspectorArray()) ;
1286 continue;
1287 }
1288 RefPtr<AsyncStackTrace> asyncStackTrace = AsyncStackTrace::create()
1289 .setCallFrames(scriptCallStack->buildInspectorArray());
1290 asyncStackTrace->setDescription(callStack->description());
1291 if (lastAsyncStackTrace)
1292 lastAsyncStackTrace->setAsyncStackTrace(asyncStackTrace);
1293 else
1294 operation->setAsyncStackTrace(asyncStackTrace);
1295 lastAsyncStackTrace = asyncStackTrace.release();
1296 }
1297
1298 if (operation)
1299 m_frontend->asyncOperationStarted(operation.release());
1300 }
1301
1302 m_asyncOperationNotifications.clear();
1303 }
1304
1305 void V8DebuggerAgent::clearCurrentAsyncOperation()
1306 {
1307 if (m_pendingTraceAsyncOperationCompleted && m_currentAsyncOperationId != un knownAsyncOperationId)
1308 traceAsyncOperationCompleted(m_currentAsyncOperationId);
1309
1310 m_currentAsyncOperationId = unknownAsyncOperationId;
1311 m_pendingTraceAsyncOperationCompleted = false;
1312 m_nestedAsyncCallCount = 0;
1313 m_currentAsyncCallChain.clear();
1314 }
1315
1316 void V8DebuggerAgent::resetAsyncCallTracker()
1317 {
1318 clearCurrentAsyncOperation();
1319 clearStepIntoAsync();
1320 for (auto& listener: m_asyncCallTrackingListeners)
1321 listener->resetAsyncOperations();
1322 m_asyncOperations.clear();
1323 m_asyncOperationNotifications.clear();
1324 m_asyncOperationBreakpoints.clear();
1325 }
1326
1327 void V8DebuggerAgent::setAsyncOperationBreakpoint(ErrorString* errorString, int operationId)
1328 {
1329 if (!trackingAsyncCalls()) {
1330 *errorString = "Can only perform operation while tracking async call sta cks.";
1331 return;
1332 }
1333 if (operationId <= 0) {
1334 *errorString = "Wrong async operation id.";
1335 return;
1336 }
1337 if (!m_asyncOperations.contains(operationId)) {
1338 *errorString = "Unknown async operation id.";
1339 return;
1340 }
1341 m_asyncOperationBreakpoints.add(operationId);
1342 }
1343
1344 void V8DebuggerAgent::removeAsyncOperationBreakpoint(ErrorString* errorString, i nt operationId)
1345 {
1346 if (!trackingAsyncCalls()) {
1347 *errorString = "Can only perform operation while tracking async call sta cks.";
1348 return;
1349 }
1350 if (operationId <= 0) {
1351 *errorString = "Wrong async operation id.";
1352 return;
1353 }
1354 m_asyncOperationBreakpoints.remove(operationId);
1355 }
1356
1357 void V8DebuggerAgent::addAsyncCallTrackingListener(AsyncCallTrackingListener* li stener)
1358 {
1359 m_asyncCallTrackingListeners.add(listener);
1360 }
1361
1362 void V8DebuggerAgent::removeAsyncCallTrackingListener(AsyncCallTrackingListener* listener)
1363 {
1364 ASSERT(m_asyncCallTrackingListeners.contains(listener));
1365 m_asyncCallTrackingListeners.remove(listener);
1366 }
1367
1368 void V8DebuggerAgent::willExecuteScript(int scriptId)
1369 {
1370 changeJavaScriptRecursionLevel(+1);
1371 // Fast return.
1372 if (m_scheduledDebuggerStep != StepInto)
1373 return;
1374 // Skip unknown scripts (e.g. InjectedScript).
1375 if (!m_scripts.contains(String::number(scriptId)))
1376 return;
1377 schedulePauseOnNextStatementIfSteppingInto();
1378 }
1379
1380 void V8DebuggerAgent::didExecuteScript()
1381 {
1382 changeJavaScriptRecursionLevel(-1);
1383 }
1384
1385 void V8DebuggerAgent::changeJavaScriptRecursionLevel(int step)
1386 {
1387 if (m_javaScriptPauseScheduled && !m_skipAllPauses && !isPaused()) {
1388 // Do not ever loose user's pause request until we have actually paused.
1389 debugger().setPauseOnNextStatement(true);
1390 }
1391 if (m_scheduledDebuggerStep == StepOut) {
1392 m_recursionLevelForStepOut += step;
1393 if (!m_recursionLevelForStepOut) {
1394 // When StepOut crosses a task boundary (i.e. js -> blink_c++) from where it was requested,
1395 // switch stepping to step into a next JS task, as if we exited to a blackboxed framework.
1396 m_scheduledDebuggerStep = StepInto;
1397 m_skipNextDebuggerStepOut = false;
1398 }
1399 }
1400 if (m_recursionLevelForStepFrame) {
1401 m_recursionLevelForStepFrame += step;
1402 if (!m_recursionLevelForStepFrame) {
1403 // We have walked through a blackboxed framework and got back to whe re we started.
1404 // If there was no stepping scheduled, we should cancel the stepping explicitly,
1405 // since there may be a scheduled StepFrame left.
1406 // Otherwise, if we were stepping in/over, the StepFrame will stop a t the right location,
1407 // whereas if we were stepping out, we should continue doing so afte r debugger pauses
1408 // from the old StepFrame.
1409 m_skippedStepFrameCount = 0;
1410 if (m_scheduledDebuggerStep == NoStep)
1411 debugger().clearStepping();
1412 else if (m_scheduledDebuggerStep == StepOut)
1413 m_skipNextDebuggerStepOut = true;
1414 }
1415 }
1416 }
1417
1418 PassRefPtr<Array<CallFrame>> V8DebuggerAgent::currentCallFrames()
1419 {
1420 if (!m_pausedScriptState || m_currentCallStack.IsEmpty())
1421 return Array<CallFrame>::create();
1422 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(m _pausedScriptState.get());
1423 if (injectedScript.isEmpty()) {
1424 ASSERT_NOT_REACHED();
1425 return Array<CallFrame>::create();
1426 }
1427
1428 v8::HandleScope scope(m_isolate);
1429 v8::Local<v8::Object> currentCallStack = m_currentCallStack.Get(m_isolate);
1430 return injectedScript.wrapCallFrames(currentCallStack, 0);
1431 }
1432
1433 PassRefPtr<StackTrace> V8DebuggerAgent::currentAsyncStackTrace()
1434 {
1435 if (!m_pausedScriptState || !trackingAsyncCalls())
1436 return nullptr;
1437 const AsyncCallChain* chain = m_currentAsyncCallChain.get();
1438 if (!chain)
1439 return nullptr;
1440 const AsyncCallStackVector& callStacks = chain->callStacks();
1441 if (callStacks.isEmpty())
1442 return nullptr;
1443 RefPtr<StackTrace> result;
1444 int asyncOrdinal = callStacks.size();
1445 for (AsyncCallStackVector::const_reverse_iterator it = callStacks.rbegin(); it != callStacks.rend(); ++it, --asyncOrdinal) {
1446 v8::HandleScope scope(m_isolate);
1447 v8::Local<v8::Object> callFrames = (*it)->callFrames(m_isolate);
1448 ScriptState* scriptState = ScriptState::from(callFrames->CreationContex t());
1449 InjectedScript injectedScript = scriptState ? m_injectedScriptManager->i njectedScriptFor(scriptState) : InjectedScript();
1450 if (injectedScript.isEmpty()) {
1451 result.clear();
1452 continue;
1453 }
1454 RefPtr<StackTrace> next = StackTrace::create()
1455 .setCallFrames(injectedScript.wrapCallFrames(callFrames, asyncOrdina l))
1456 .release();
1457 next->setDescription((*it)->description());
1458 if (result)
1459 next->setAsyncStackTrace(result.release());
1460 result.swap(next);
1461 }
1462 return result.release();
1463 }
1464
1465 PassRefPtrWillBeRawPtr<ScriptAsyncCallStack> V8DebuggerAgent::currentAsyncStackT raceForConsole()
1466 {
1467 if (!trackingAsyncCalls())
1468 return nullptr;
1469 const AsyncCallChain* chain = m_currentAsyncCallChain.get();
1470 if (!chain)
1471 return nullptr;
1472 const AsyncCallStackVector& callStacks = chain->callStacks();
1473 if (callStacks.isEmpty())
1474 return nullptr;
1475 RefPtrWillBeRawPtr<ScriptAsyncCallStack> result = nullptr;
1476 for (AsyncCallStackVector::const_reverse_iterator it = callStacks.rbegin(); it != callStacks.rend(); ++it) {
1477 v8::HandleScope scope(m_isolate);
1478 RefPtr<JavaScriptCallFrame> callFrame = toJavaScriptCallFrame((*it)->cal lFrames(m_isolate));
1479 if (!callFrame)
1480 break;
1481 result = ScriptAsyncCallStack::create((*it)->description(), toScriptCall Stack(callFrame.get()), result.release());
1482 }
1483 return result.release();
1484 }
1485
1486 String V8DebuggerAgent::sourceMapURLForScript(const Script& script, CompileResul t compileResult)
1487 {
1488 bool hasSyntaxError = compileResult != CompileSuccess;
1489 if (!hasSyntaxError)
1490 return script.sourceMappingURL();
1491 return ContentSearchUtils::findSourceMapURL(script.source(), ContentSearchUt ils::JavaScriptMagicComment);
1492 }
1493
1494 // V8DebuggerListener functions
1495
1496 void V8DebuggerAgent::didParseSource(const ParsedScript& parsedScript)
1497 {
1498 Script script = parsedScript.script;
1499
1500 bool hasSyntaxError = parsedScript.compileResult != CompileSuccess;
1501 if (hasSyntaxError)
1502 script.setSourceURL(ContentSearchUtils::findSourceURL(script.source(), C ontentSearchUtils::JavaScriptMagicComment));
1503
1504 bool isContentScript = script.isContentScript();
1505 bool isInternalScript = script.isInternalScript();
1506 bool hasSourceURL = script.hasSourceURL();
1507 String scriptURL = script.sourceURL();
1508 String sourceMapURL = sourceMapURLForScript(script, parsedScript.compileResu lt);
1509
1510 const String* sourceMapURLParam = sourceMapURL.isNull() ? nullptr : &sourceM apURL;
1511 const bool* isContentScriptParam = isContentScript ? &isContentScript : null ptr;
1512 const bool* isInternalScriptParam = isInternalScript ? &isInternalScript : n ullptr;
1513 const bool* hasSourceURLParam = hasSourceURL ? &hasSourceURL : nullptr;
1514 if (!hasSyntaxError)
1515 m_frontend->scriptParsed(parsedScript.scriptId, scriptURL, script.startL ine(), script.startColumn(), script.endLine(), script.endColumn(), isContentScri ptParam, isInternalScriptParam, sourceMapURLParam, hasSourceURLParam);
1516 else
1517 m_frontend->scriptFailedToParse(parsedScript.scriptId, scriptURL, script .startLine(), script.startColumn(), script.endLine(), script.endColumn(), isCont entScriptParam, isInternalScriptParam, sourceMapURLParam, hasSourceURLParam);
1518
1519 m_scripts.set(parsedScript.scriptId, script);
1520
1521 if (scriptURL.isEmpty() || hasSyntaxError)
1522 return;
1523
1524 RefPtr<JSONObject> breakpointsCookie = m_state->getObject(DebuggerAgentState ::javaScriptBreakpoints);
1525 for (auto& cookie : *breakpointsCookie) {
1526 RefPtr<JSONObject> breakpointObject = cookie.value->asObject();
1527 bool isRegex;
1528 breakpointObject->getBoolean(DebuggerAgentState::isRegex, &isRegex);
1529 String url;
1530 breakpointObject->getString(DebuggerAgentState::url, &url);
1531 if (!matches(scriptURL, url, isRegex))
1532 continue;
1533 ScriptBreakpoint breakpoint;
1534 breakpointObject->getNumber(DebuggerAgentState::lineNumber, &breakpoint. lineNumber);
1535 breakpointObject->getNumber(DebuggerAgentState::columnNumber, &breakpoin t.columnNumber);
1536 breakpointObject->getString(DebuggerAgentState::condition, &breakpoint.c ondition);
1537 RefPtr<TypeBuilder::Debugger::Location> location = resolveBreakpoint(coo kie.key, parsedScript.scriptId, breakpoint, UserBreakpointSource);
1538 if (location)
1539 m_frontend->breakpointResolved(cookie.key, location);
1540 }
1541 }
1542
1543 V8DebuggerListener::SkipPauseRequest V8DebuggerAgent::didPause(v8::Local<v8::Con text> context, v8::Local<v8::Object> callFrames, v8::Local<v8::Value> v8exceptio n, const Vector<String>& hitBreakpoints, bool isPromiseRejection)
1544 {
1545 ScriptState* scriptState = ScriptState::from(context);
1546 ScriptValue exception(scriptState, v8exception);
1547
1548 V8DebuggerListener::SkipPauseRequest result;
1549 if (m_skipAllPauses)
1550 result = V8DebuggerListener::Continue;
1551 else if (!hitBreakpoints.isEmpty())
1552 result = V8DebuggerListener::NoSkip; // Don't skip explicit breakpoints even if set in frameworks.
1553 else if (!exception.isEmpty())
1554 result = shouldSkipExceptionPause();
1555 else if (m_scheduledDebuggerStep != NoStep || m_javaScriptPauseScheduled || m_pausingOnNativeEvent)
1556 result = shouldSkipStepPause();
1557 else
1558 result = V8DebuggerListener::NoSkip;
1559
1560 m_skipNextDebuggerStepOut = false;
1561 if (result != V8DebuggerListener::NoSkip)
1562 return result;
1563
1564 // Skip pauses inside V8 internal scripts and on syntax errors.
1565 if (callFrames.IsEmpty())
1566 return V8DebuggerListener::Continue;
1567
1568 ASSERT(scriptState);
1569 ASSERT(!m_pausedScriptState);
1570 m_pausedScriptState = scriptState;
1571 m_currentCallStack.Reset(m_isolate, callFrames);
1572
1573 if (!exception.isEmpty()) {
1574 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptF or(scriptState);
1575 if (!injectedScript.isEmpty()) {
1576 m_breakReason = isPromiseRejection ? InspectorFrontend::Debugger::Re ason::PromiseRejection : InspectorFrontend::Debugger::Reason::Exception;
1577 m_breakAuxData = injectedScript.wrapObject(exception, V8DebuggerAgen t::backtraceObjectGroup)->openAccessors();
1578 // m_breakAuxData might be null after this.
1579 }
1580 } else if (m_pausingOnAsyncOperation) {
1581 m_breakReason = InspectorFrontend::Debugger::Reason::AsyncOperation;
1582 m_breakAuxData = JSONObject::create();
1583 m_breakAuxData->setNumber("operationId", m_currentAsyncOperationId);
1584 }
1585
1586 RefPtr<Array<String>> hitBreakpointIds = Array<String>::create();
1587
1588 for (const auto& point : hitBreakpoints) {
1589 DebugServerBreakpointToBreakpointIdAndSourceMap::iterator breakpointIter ator = m_serverBreakpoints.find(point);
1590 if (breakpointIterator != m_serverBreakpoints.end()) {
1591 const String& localId = breakpointIterator->value.first;
1592 hitBreakpointIds->addItem(localId);
1593
1594 BreakpointSource source = breakpointIterator->value.second;
1595 if (m_breakReason == InspectorFrontend::Debugger::Reason::Other && s ource == DebugCommandBreakpointSource)
1596 m_breakReason = InspectorFrontend::Debugger::Reason::DebugComman d;
1597 }
1598 }
1599
1600 if (!m_asyncOperationNotifications.isEmpty())
1601 flushAsyncOperationEvents(nullptr);
1602
1603 m_frontend->paused(currentCallFrames(), m_breakReason, m_breakAuxData, hitBr eakpointIds, currentAsyncStackTrace());
1604 m_scheduledDebuggerStep = NoStep;
1605 m_javaScriptPauseScheduled = false;
1606 m_steppingFromFramework = false;
1607 m_pausingOnNativeEvent = false;
1608 m_skippedStepFrameCount = 0;
1609 m_recursionLevelForStepFrame = 0;
1610 clearStepIntoAsync();
1611
1612 if (!m_continueToLocationBreakpointId.isEmpty()) {
1613 debugger().removeBreakpoint(m_continueToLocationBreakpointId);
1614 m_continueToLocationBreakpointId = "";
1615 }
1616 return result;
1617 }
1618
1619 void V8DebuggerAgent::didContinue()
1620 {
1621 m_pausedScriptState = nullptr;
1622 m_currentCallStack.Reset();
1623 clearBreakDetails();
1624 m_frontend->resumed();
1625 }
1626
1627 bool V8DebuggerAgent::canBreakProgram()
1628 {
1629 return debugger().canBreakProgram();
1630 }
1631
1632 void V8DebuggerAgent::breakProgram(InspectorFrontend::Debugger::Reason::Enum bre akReason, PassRefPtr<JSONObject> data)
1633 {
1634 ASSERT(enabled());
1635 if (m_skipAllPauses || m_pausedScriptState || isCallStackEmptyOrBlackboxed() )
1636 return;
1637 m_breakReason = breakReason;
1638 m_breakAuxData = data;
1639 m_scheduledDebuggerStep = NoStep;
1640 m_steppingFromFramework = false;
1641 m_pausingOnNativeEvent = false;
1642 clearStepIntoAsync();
1643 debugger().breakProgram();
1644 }
1645
1646 void V8DebuggerAgent::clearStepIntoAsync()
1647 {
1648 m_startingStepIntoAsync = false;
1649 m_pausingOnAsyncOperation = false;
1650 m_pausingAsyncOperations.clear();
1651 }
1652
1653 bool V8DebuggerAgent::assertPaused(ErrorString* errorString)
1654 {
1655 if (!m_pausedScriptState) {
1656 *errorString = "Can only perform operation while paused.";
1657 return false;
1658 }
1659 return true;
1660 }
1661
1662 void V8DebuggerAgent::clearBreakDetails()
1663 {
1664 m_breakReason = InspectorFrontend::Debugger::Reason::Other;
1665 m_breakAuxData = nullptr;
1666 }
1667
1668 void V8DebuggerAgent::setBreakpoint(const String& scriptId, int lineNumber, int columnNumber, BreakpointSource source, const String& condition)
1669 {
1670 String breakpointId = generateBreakpointId(scriptId, lineNumber, columnNumbe r, source);
1671 ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition);
1672 resolveBreakpoint(breakpointId, scriptId, breakpoint, source);
1673 }
1674
1675 void V8DebuggerAgent::removeBreakpoint(const String& scriptId, int lineNumber, i nt columnNumber, BreakpointSource source)
1676 {
1677 removeBreakpoint(generateBreakpointId(scriptId, lineNumber, columnNumber, so urce));
1678 }
1679
1680 void V8DebuggerAgent::reset()
1681 {
1682 m_scheduledDebuggerStep = NoStep;
1683 m_scripts.clear();
1684 m_breakpointIdToDebuggerBreakpointIds.clear();
1685 resetAsyncCallTracker();
1686 m_promiseTracker->clear();
1687 if (m_frontend)
1688 m_frontend->globalObjectCleared();
1689 }
1690
1691 PassRefPtr<TypeBuilder::Debugger::ExceptionDetails> V8DebuggerAgent::createExcep tionDetails(v8::Isolate* isolate, v8::Local<v8::Message> message)
1692 {
1693 RefPtr<ExceptionDetails> exceptionDetails = ExceptionDetails::create().setTe xt(toCoreStringWithUndefinedOrNullCheck(message->Get()));
1694 exceptionDetails->setLine(message->GetLineNumber());
1695 exceptionDetails->setColumn(message->GetStartColumn());
1696 v8::Local<v8::StackTrace> messageStackTrace = message->GetStackTrace();
1697 if (!messageStackTrace.IsEmpty() && messageStackTrace->GetFrameCount() > 0)
1698 exceptionDetails->setStackTrace(createScriptCallStack(isolate, messageSt ackTrace, messageStackTrace->GetFrameCount())->buildInspectorArray());
1699 return exceptionDetails.release();
1700 }
1701
1702 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698