Web Inspector: support breakpoints for timers and animation-frame events
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Controllers / DebuggerManager.js
1 /*
2  * Copyright (C) 2013-2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 WI.DebuggerManager = class DebuggerManager extends WI.Object
27 {
28     constructor()
29     {
30         super();
31
32         DebuggerAgent.enable();
33
34         WI.notifications.addEventListener(WI.Notification.DebugUIEnabledDidChange, this._debugUIEnabledDidChange, this);
35
36         WI.Breakpoint.addEventListener(WI.Breakpoint.Event.DisplayLocationDidChange, this._breakpointDisplayLocationDidChange, this);
37         WI.Breakpoint.addEventListener(WI.Breakpoint.Event.DisabledStateDidChange, this._breakpointDisabledStateDidChange, this);
38         WI.Breakpoint.addEventListener(WI.Breakpoint.Event.ConditionDidChange, this._breakpointEditablePropertyDidChange, this);
39         WI.Breakpoint.addEventListener(WI.Breakpoint.Event.IgnoreCountDidChange, this._breakpointEditablePropertyDidChange, this);
40         WI.Breakpoint.addEventListener(WI.Breakpoint.Event.AutoContinueDidChange, this._breakpointEditablePropertyDidChange, this);
41         WI.Breakpoint.addEventListener(WI.Breakpoint.Event.ActionsDidChange, this._breakpointEditablePropertyDidChange, this);
42
43         WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingWillStart, this._timelineCapturingWillStart, this);
44         WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStopped, this._timelineCapturingStopped, this);
45
46         WI.targetManager.addEventListener(WI.TargetManager.Event.TargetRemoved, this._targetRemoved, this);
47
48         WI.Frame.addEventListener(WI.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
49
50         this._breakpointsSetting = new WI.Setting("breakpoints", []);
51         this._breakpointsEnabledSetting = new WI.Setting("breakpoints-enabled", true);
52         this._allExceptionsBreakpointEnabledSetting = new WI.Setting("break-on-all-exceptions", false);
53         this._allUncaughtExceptionsBreakpointEnabledSetting = new WI.Setting("break-on-all-uncaught-exceptions", false);
54         this._assertionsBreakpointEnabledSetting = new WI.Setting("break-on-assertions", false);
55         this._asyncStackTraceDepthSetting = new WI.Setting("async-stack-trace-depth", 200);
56
57         let specialBreakpointLocation = new WI.SourceCodeLocation(null, Infinity, Infinity);
58
59         this._allExceptionsBreakpoint = new WI.Breakpoint(specialBreakpointLocation, !this._allExceptionsBreakpointEnabledSetting.value);
60         this._allExceptionsBreakpoint.resolved = true;
61
62         this._allUncaughtExceptionsBreakpoint = new WI.Breakpoint(specialBreakpointLocation, !this._allUncaughtExceptionsBreakpointEnabledSetting.value);
63
64         this._assertionsBreakpoint = new WI.Breakpoint(specialBreakpointLocation, !this._assertionsBreakpointEnabledSetting.value);
65         this._assertionsBreakpoint.resolved = true;
66
67         this._breakpoints = [];
68         this._breakpointContentIdentifierMap = new Map;
69         this._breakpointScriptIdentifierMap = new Map;
70         this._breakpointIdMap = new Map;
71
72         this._breakOnExceptionsState = "none";
73         this._updateBreakOnExceptionsState();
74
75         this._nextBreakpointActionIdentifier = 1;
76
77         this._activeCallFrame = null;
78
79         this._internalWebKitScripts = [];
80         this._targetDebuggerDataMap = new Map;
81         this._targetDebuggerDataMap.set(WI.mainTarget, new WI.DebuggerData(WI.mainTarget));
82
83         // Restore the correct breakpoints enabled setting if Web Inspector had
84         // previously been left in a state where breakpoints were temporarily disabled.
85         this._temporarilyDisabledBreakpointsRestoreSetting = new WI.Setting("temporarily-disabled-breakpoints-restore", null);
86         if (this._temporarilyDisabledBreakpointsRestoreSetting.value !== null) {
87             this._breakpointsEnabledSetting.value = this._temporarilyDisabledBreakpointsRestoreSetting.value;
88             this._temporarilyDisabledBreakpointsRestoreSetting.value = null;
89         }
90
91         DebuggerAgent.setBreakpointsActive(this._breakpointsEnabledSetting.value);
92         DebuggerAgent.setPauseOnExceptions(this._breakOnExceptionsState);
93
94         // COMPATIBILITY (iOS 10): DebuggerAgent.setPauseOnAssertions did not exist yet.
95         if (DebuggerAgent.setPauseOnAssertions)
96             DebuggerAgent.setPauseOnAssertions(this._assertionsBreakpointEnabledSetting.value);
97
98         // COMPATIBILITY (iOS 10): Debugger.setAsyncStackTraceDepth did not exist yet.
99         if (DebuggerAgent.setAsyncStackTraceDepth)
100             DebuggerAgent.setAsyncStackTraceDepth(this._asyncStackTraceDepthSetting.value);
101
102         // COMPATIBILITY (iOS 12): DebuggerAgent.setPauseForInternalScripts did not exist yet.
103         if (DebuggerAgent.setPauseForInternalScripts) {
104             let updateBackendSetting = () => { DebuggerAgent.setPauseForInternalScripts(WI.settings.pauseForInternalScripts.value); };
105             WI.settings.pauseForInternalScripts.addEventListener(WI.Setting.Event.Changed, updateBackendSetting);
106
107             updateBackendSetting();
108         }
109
110         this._ignoreBreakpointDisplayLocationDidChangeEvent = false;
111
112         function restoreBreakpointsSoon() {
113             this._restoringBreakpoints = true;
114             for (let cookie of this._breakpointsSetting.value)
115                 this.addBreakpoint(new WI.Breakpoint(cookie));
116             this._restoringBreakpoints = false;
117         }
118
119         // Ensure that all managers learn about restored breakpoints,
120         // regardless of their initialization order.
121         setTimeout(restoreBreakpointsSoon.bind(this), 0);
122     }
123
124     // Public
125
126     get paused()
127     {
128         for (let [target, targetData] of this._targetDebuggerDataMap) {
129             if (targetData.paused)
130                 return true;
131         }
132
133         return false;
134     }
135
136     get activeCallFrame()
137     {
138         return this._activeCallFrame;
139     }
140
141     set activeCallFrame(callFrame)
142     {
143         if (callFrame === this._activeCallFrame)
144             return;
145
146         this._activeCallFrame = callFrame || null;
147
148         this.dispatchEventToListeners(WI.DebuggerManager.Event.ActiveCallFrameDidChange);
149     }
150
151     dataForTarget(target)
152     {
153         let targetData = this._targetDebuggerDataMap.get(target);
154         if (targetData)
155             return targetData;
156
157         targetData = new WI.DebuggerData(target);
158         this._targetDebuggerDataMap.set(target, targetData);
159         return targetData;
160     }
161
162     get allExceptionsBreakpoint()
163     {
164         return this._allExceptionsBreakpoint;
165     }
166
167     get allUncaughtExceptionsBreakpoint()
168     {
169         return this._allUncaughtExceptionsBreakpoint;
170     }
171
172     get assertionsBreakpoint()
173     {
174         return this._assertionsBreakpoint;
175     }
176
177     get breakpoints()
178     {
179         return this._breakpoints;
180     }
181
182     breakpointForIdentifier(id)
183     {
184         return this._breakpointIdMap.get(id) || null;
185     }
186
187     breakpointsForSourceCode(sourceCode)
188     {
189         console.assert(sourceCode instanceof WI.Resource || sourceCode instanceof WI.Script);
190
191         if (sourceCode instanceof WI.SourceMapResource) {
192             let originalSourceCodeBreakpoints = this.breakpointsForSourceCode(sourceCode.sourceMap.originalSourceCode);
193             return originalSourceCodeBreakpoints.filter(function(breakpoint) {
194                 return breakpoint.sourceCodeLocation.displaySourceCode === sourceCode;
195             });
196         }
197
198         let contentIdentifierBreakpoints = this._breakpointContentIdentifierMap.get(sourceCode.contentIdentifier);
199         if (contentIdentifierBreakpoints) {
200             this._associateBreakpointsWithSourceCode(contentIdentifierBreakpoints, sourceCode);
201             return contentIdentifierBreakpoints;
202         }
203
204         if (sourceCode instanceof WI.Script) {
205             let scriptIdentifierBreakpoints = this._breakpointScriptIdentifierMap.get(sourceCode.id);
206             if (scriptIdentifierBreakpoints) {
207                 this._associateBreakpointsWithSourceCode(scriptIdentifierBreakpoints, sourceCode);
208                 return scriptIdentifierBreakpoints;
209             }
210         }
211
212         return [];
213     }
214
215     breakpointForSourceCodeLocation(sourceCodeLocation)
216     {
217         console.assert(sourceCodeLocation instanceof WI.SourceCodeLocation);
218
219         for (let breakpoint of this.breakpointsForSourceCode(sourceCodeLocation.sourceCode)) {
220             if (breakpoint.sourceCodeLocation.isEqual(sourceCodeLocation))
221                 return breakpoint;
222         }
223
224         return null;
225     }
226
227     isBreakpointRemovable(breakpoint)
228     {
229         return breakpoint !== this._allExceptionsBreakpoint
230             && breakpoint !== this._allUncaughtExceptionsBreakpoint
231             && breakpoint !== this._assertionsBreakpoint;
232     }
233
234     isBreakpointEditable(breakpoint)
235     {
236         return this.isBreakpointRemovable(breakpoint);
237     }
238
239     get breakpointsEnabled()
240     {
241         return this._breakpointsEnabledSetting.value;
242     }
243
244     set breakpointsEnabled(enabled)
245     {
246         if (this._breakpointsEnabledSetting.value === enabled)
247             return;
248
249         console.assert(!(enabled && this.breakpointsDisabledTemporarily), "Should not enable breakpoints when we are temporarily disabling breakpoints.");
250         if (enabled && this.breakpointsDisabledTemporarily)
251             return;
252
253         this._breakpointsEnabledSetting.value = enabled;
254
255         this._updateBreakOnExceptionsState();
256
257         for (let target of WI.targets) {
258             target.DebuggerAgent.setBreakpointsActive(enabled);
259             target.DebuggerAgent.setPauseOnExceptions(this._breakOnExceptionsState);
260         }
261
262         this.dispatchEventToListeners(WI.DebuggerManager.Event.BreakpointsEnabledDidChange);
263     }
264
265     get breakpointsDisabledTemporarily()
266     {
267         return this._temporarilyDisabledBreakpointsRestoreSetting.value !== null;
268     }
269
270     scriptForIdentifier(id, target)
271     {
272         console.assert(target instanceof WI.Target);
273         return this.dataForTarget(target).scriptForIdentifier(id);
274     }
275
276     scriptsForURL(url, target)
277     {
278         // FIXME: This may not be safe. A Resource's URL may differ from a Script's URL.
279         console.assert(target instanceof WI.Target);
280         return this.dataForTarget(target).scriptsForURL(url);
281     }
282
283     get searchableScripts()
284     {
285         return this.knownNonResourceScripts.filter((script) => !!script.contentIdentifier);
286     }
287
288     get knownNonResourceScripts()
289     {
290         let knownScripts = [];
291
292         for (let [target, targetData] of this._targetDebuggerDataMap) {
293             for (let script of targetData.scripts) {
294                 if (script.resource)
295                     continue;
296                 if (isWebInspectorConsoleEvaluationScript(script.sourceURL))
297                     continue;
298                 if (!WI.isDebugUIEnabled() && isWebKitInternalScript(script.sourceURL))
299                     continue;
300                 knownScripts.push(script);
301             }
302         }
303
304         return knownScripts;
305     }
306
307     get asyncStackTraceDepth()
308     {
309         return this._asyncStackTraceDepthSetting.value;
310     }
311
312     set asyncStackTraceDepth(x)
313     {
314         if (this._asyncStackTraceDepthSetting.value === x)
315             return;
316
317         this._asyncStackTraceDepthSetting.value = x;
318
319         for (let target of WI.targets)
320             target.DebuggerAgent.setAsyncStackTraceDepth(this._asyncStackTraceDepthSetting.value);
321     }
322
323     pause()
324     {
325         if (this.paused)
326             return Promise.resolve();
327
328         this.dispatchEventToListeners(WI.DebuggerManager.Event.WaitingToPause);
329
330         let listener = new WI.EventListener(this, true);
331
332         let managerResult = new Promise(function(resolve, reject) {
333             listener.connect(WI.debuggerManager, WI.DebuggerManager.Event.Paused, resolve);
334         });
335
336         let promises = [];
337         for (let [target, targetData] of this._targetDebuggerDataMap)
338             promises.push(targetData.pauseIfNeeded());
339
340         return Promise.all([managerResult, ...promises]);
341     }
342
343     resume()
344     {
345         if (!this.paused)
346             return Promise.resolve();
347
348         let listener = new WI.EventListener(this, true);
349
350         let managerResult = new Promise(function(resolve, reject) {
351             listener.connect(WI.debuggerManager, WI.DebuggerManager.Event.Resumed, resolve);
352         });
353
354         let promises = [];
355         for (let [target, targetData] of this._targetDebuggerDataMap)
356             promises.push(targetData.resumeIfNeeded());
357
358         return Promise.all([managerResult, ...promises]);
359     }
360
361     stepOver()
362     {
363         if (!this.paused)
364             return Promise.reject(new Error("Cannot step over because debugger is not paused."));
365
366         let listener = new WI.EventListener(this, true);
367
368         let managerResult = new Promise(function(resolve, reject) {
369             listener.connect(WI.debuggerManager, WI.DebuggerManager.Event.ActiveCallFrameDidChange, resolve);
370         });
371
372         let protocolResult = this._activeCallFrame.target.DebuggerAgent.stepOver()
373             .catch(function(error) {
374                 listener.disconnect();
375                 console.error("DebuggerManager.stepOver failed: ", error);
376                 throw error;
377             });
378
379         return Promise.all([managerResult, protocolResult]);
380     }
381
382     stepInto()
383     {
384         if (!this.paused)
385             return Promise.reject(new Error("Cannot step into because debugger is not paused."));
386
387         let listener = new WI.EventListener(this, true);
388
389         let managerResult = new Promise(function(resolve, reject) {
390             listener.connect(WI.debuggerManager, WI.DebuggerManager.Event.ActiveCallFrameDidChange, resolve);
391         });
392
393         let protocolResult = this._activeCallFrame.target.DebuggerAgent.stepInto()
394             .catch(function(error) {
395                 listener.disconnect();
396                 console.error("DebuggerManager.stepInto failed: ", error);
397                 throw error;
398             });
399
400         return Promise.all([managerResult, protocolResult]);
401     }
402
403     stepOut()
404     {
405         if (!this.paused)
406             return Promise.reject(new Error("Cannot step out because debugger is not paused."));
407
408         let listener = new WI.EventListener(this, true);
409
410         let managerResult = new Promise(function(resolve, reject) {
411             listener.connect(WI.debuggerManager, WI.DebuggerManager.Event.ActiveCallFrameDidChange, resolve);
412         });
413
414         let protocolResult = this._activeCallFrame.target.DebuggerAgent.stepOut()
415             .catch(function(error) {
416                 listener.disconnect();
417                 console.error("DebuggerManager.stepOut failed: ", error);
418                 throw error;
419             });
420
421         return Promise.all([managerResult, protocolResult]);
422     }
423
424     continueUntilNextRunLoop(target)
425     {
426         return this.dataForTarget(target).continueUntilNextRunLoop();
427     }
428
429     continueToLocation(script, lineNumber, columnNumber)
430     {
431         return script.target.DebuggerAgent.continueToLocation({scriptId: script.id, lineNumber, columnNumber});
432     }
433
434     addBreakpoint(breakpoint, shouldSpeculativelyResolve)
435     {
436         console.assert(breakpoint instanceof WI.Breakpoint);
437         if (!breakpoint)
438             return;
439
440         if (breakpoint.contentIdentifier) {
441             let contentIdentifierBreakpoints = this._breakpointContentIdentifierMap.get(breakpoint.contentIdentifier);
442             if (!contentIdentifierBreakpoints) {
443                 contentIdentifierBreakpoints = [];
444                 this._breakpointContentIdentifierMap.set(breakpoint.contentIdentifier, contentIdentifierBreakpoints);
445             }
446             contentIdentifierBreakpoints.push(breakpoint);
447         }
448
449         if (breakpoint.scriptIdentifier) {
450             let scriptIdentifierBreakpoints = this._breakpointScriptIdentifierMap.get(breakpoint.scriptIdentifier);
451             if (!scriptIdentifierBreakpoints) {
452                 scriptIdentifierBreakpoints = [];
453                 this._breakpointScriptIdentifierMap.set(breakpoint.scriptIdentifier, scriptIdentifierBreakpoints);
454             }
455             scriptIdentifierBreakpoints.push(breakpoint);
456         }
457
458         this._breakpoints.push(breakpoint);
459
460         if (!breakpoint.disabled) {
461             const specificTarget = undefined;
462             this._setBreakpoint(breakpoint, specificTarget, () => {
463                 if (shouldSpeculativelyResolve)
464                     breakpoint.resolved = true;
465             });
466         }
467
468         this._saveBreakpoints();
469
470         this.dispatchEventToListeners(WI.DebuggerManager.Event.BreakpointAdded, {breakpoint});
471     }
472
473     removeBreakpoint(breakpoint)
474     {
475         console.assert(breakpoint instanceof WI.Breakpoint);
476         if (!breakpoint)
477             return;
478
479         console.assert(this.isBreakpointRemovable(breakpoint));
480         if (!this.isBreakpointRemovable(breakpoint))
481             return;
482
483         this._breakpoints.remove(breakpoint);
484
485         if (breakpoint.identifier)
486             this._removeBreakpoint(breakpoint);
487
488         if (breakpoint.contentIdentifier) {
489             let contentIdentifierBreakpoints = this._breakpointContentIdentifierMap.get(breakpoint.contentIdentifier);
490             if (contentIdentifierBreakpoints) {
491                 contentIdentifierBreakpoints.remove(breakpoint);
492                 if (!contentIdentifierBreakpoints.length)
493                     this._breakpointContentIdentifierMap.delete(breakpoint.contentIdentifier);
494             }
495         }
496
497         if (breakpoint.scriptIdentifier) {
498             let scriptIdentifierBreakpoints = this._breakpointScriptIdentifierMap.get(breakpoint.scriptIdentifier);
499             if (scriptIdentifierBreakpoints) {
500                 scriptIdentifierBreakpoints.remove(breakpoint);
501                 if (!scriptIdentifierBreakpoints.length)
502                     this._breakpointScriptIdentifierMap.delete(breakpoint.scriptIdentifier);
503             }
504         }
505
506         // Disable the breakpoint first, so removing actions doesn't re-add the breakpoint.
507         breakpoint.disabled = true;
508         breakpoint.clearActions();
509
510         this._saveBreakpoints();
511
512         this.dispatchEventToListeners(WI.DebuggerManager.Event.BreakpointRemoved, {breakpoint});
513     }
514
515     nextBreakpointActionIdentifier()
516     {
517         return this._nextBreakpointActionIdentifier++;
518     }
519
520     initializeTarget(target)
521     {
522         let DebuggerAgent = target.DebuggerAgent;
523         let targetData = this.dataForTarget(target);
524
525         // Initialize global state.
526         DebuggerAgent.enable();
527         DebuggerAgent.setBreakpointsActive(this._breakpointsEnabledSetting.value);
528         DebuggerAgent.setPauseOnAssertions(this._assertionsBreakpointEnabledSetting.value);
529         DebuggerAgent.setPauseOnExceptions(this._breakOnExceptionsState);
530         DebuggerAgent.setAsyncStackTraceDepth(this._asyncStackTraceDepthSetting.value);
531
532         if (this.paused)
533             targetData.pauseIfNeeded();
534
535         // Initialize breakpoints.
536         this._restoringBreakpoints = true;
537         for (let breakpoint of this._breakpoints) {
538             if (breakpoint.disabled)
539                 continue;
540             if (!breakpoint.contentIdentifier)
541                 continue;
542             this._setBreakpoint(breakpoint, target);
543         }
544         this._restoringBreakpoints = false;
545     }
546
547     // Protected (Called from WI.DebuggerObserver)
548
549     breakpointResolved(target, breakpointIdentifier, location)
550     {
551         // Called from WI.DebuggerObserver.
552
553         let breakpoint = this._breakpointIdMap.get(breakpointIdentifier);
554         console.assert(breakpoint);
555         if (!breakpoint)
556             return;
557
558         console.assert(breakpoint.identifier === breakpointIdentifier);
559
560         if (!breakpoint.sourceCodeLocation.sourceCode) {
561             let sourceCodeLocation = this._sourceCodeLocationFromPayload(target, location);
562             breakpoint.sourceCodeLocation.sourceCode = sourceCodeLocation.sourceCode;
563         }
564
565         breakpoint.resolved = true;
566     }
567
568     reset()
569     {
570         // Called from WI.DebuggerObserver.
571
572         let wasPaused = this.paused;
573
574         WI.Script.resetUniqueDisplayNameNumbers();
575
576         this._internalWebKitScripts = [];
577         this._targetDebuggerDataMap.clear();
578
579         this._ignoreBreakpointDisplayLocationDidChangeEvent = true;
580
581         // Mark all the breakpoints as unresolved. They will be reported as resolved when
582         // breakpointResolved is called as the page loads.
583         for (let breakpoint of this._breakpoints) {
584             breakpoint.resolved = false;
585             if (breakpoint.sourceCodeLocation.sourceCode)
586                 breakpoint.sourceCodeLocation.sourceCode = null;
587         }
588
589         this._ignoreBreakpointDisplayLocationDidChangeEvent = false;
590
591         this.dispatchEventToListeners(WI.DebuggerManager.Event.ScriptsCleared);
592
593         if (wasPaused)
594             this.dispatchEventToListeners(WI.DebuggerManager.Event.Resumed);
595     }
596
597     debuggerDidPause(target, callFramesPayload, reason, data, asyncStackTracePayload)
598     {
599         // Called from WI.DebuggerObserver.
600
601         if (this._delayedResumeTimeout) {
602             clearTimeout(this._delayedResumeTimeout);
603             this._delayedResumeTimeout = undefined;
604         }
605
606         let wasPaused = this.paused;
607         let targetData = this._targetDebuggerDataMap.get(target);
608
609         let callFrames = [];
610         let pauseReason = this._pauseReasonFromPayload(reason);
611         let pauseData = data || null;
612
613         for (var i = 0; i < callFramesPayload.length; ++i) {
614             var callFramePayload = callFramesPayload[i];
615             var sourceCodeLocation = this._sourceCodeLocationFromPayload(target, callFramePayload.location);
616             // FIXME: There may be useful call frames without a source code location (native callframes), should we include them?
617             if (!sourceCodeLocation)
618                 continue;
619             if (!sourceCodeLocation.sourceCode)
620                 continue;
621
622             // Exclude the case where the call frame is in the inspector code.
623             if (!WI.isDebugUIEnabled() && isWebKitInternalScript(sourceCodeLocation.sourceCode.sourceURL))
624                 continue;
625
626             let scopeChain = this._scopeChainFromPayload(target, callFramePayload.scopeChain);
627             let callFrame = WI.CallFrame.fromDebuggerPayload(target, callFramePayload, scopeChain, sourceCodeLocation);
628             callFrames.push(callFrame);
629         }
630
631         let activeCallFrame = callFrames[0];
632
633         if (!activeCallFrame) {
634             // FIXME: This may not be safe for multiple threads/targets.
635             // This indicates we were pausing in internal scripts only (Injected Scripts).
636             // Just resume and skip past this pause. We should be fixing the backend to
637             // not send such pauses.
638             if (wasPaused)
639                 target.DebuggerAgent.continueUntilNextRunLoop();
640             else
641                 target.DebuggerAgent.resume();
642             this._didResumeInternal(target);
643             return;
644         }
645
646         let asyncStackTrace = WI.StackTrace.fromPayload(target, asyncStackTracePayload);
647         targetData.updateForPause(callFrames, pauseReason, pauseData, asyncStackTrace);
648
649         // Pause other targets because at least one target has paused.
650         // FIXME: Should this be done on the backend?
651         for (let [otherTarget, otherTargetData] of this._targetDebuggerDataMap)
652             otherTargetData.pauseIfNeeded();
653
654         let activeCallFrameDidChange = this._activeCallFrame && this._activeCallFrame.target === target;
655         if (activeCallFrameDidChange)
656             this._activeCallFrame = activeCallFrame;
657         else if (!wasPaused) {
658             this._activeCallFrame = activeCallFrame;
659             activeCallFrameDidChange = true;
660         }
661
662         if (!wasPaused)
663             this.dispatchEventToListeners(WI.DebuggerManager.Event.Paused);
664
665         this.dispatchEventToListeners(WI.DebuggerManager.Event.CallFramesDidChange, {target});
666
667         if (activeCallFrameDidChange)
668             this.dispatchEventToListeners(WI.DebuggerManager.Event.ActiveCallFrameDidChange);
669     }
670
671     debuggerDidResume(target)
672     {
673         // Called from WI.DebuggerObserver.
674
675         // COMPATIBILITY (iOS 10): Debugger.resumed event was ambiguous. When stepping
676         // we would receive a Debugger.resumed and we would not know if it really meant
677         // the backend resumed or would pause again due to a step. Legacy backends wait
678         // 50ms, and treat it as a real resume if we haven't paused in that time frame.
679         // This delay ensures the user interface does not flash between brief steps
680         // or successive breakpoints.
681         if (!DebuggerAgent.setPauseOnAssertions) {
682             this._delayedResumeTimeout = setTimeout(this._didResumeInternal.bind(this, target), 50);
683             return;
684         }
685
686         this._didResumeInternal(target);
687     }
688
689     playBreakpointActionSound(breakpointActionIdentifier)
690     {
691         // Called from WI.DebuggerObserver.
692
693         InspectorFrontendHost.beep();
694     }
695
696     scriptDidParse(target, scriptIdentifier, url, startLine, startColumn, endLine, endColumn, isModule, isContentScript, sourceURL, sourceMapURL)
697     {
698         // Called from WI.DebuggerObserver.
699
700         // Don't add the script again if it is already known.
701         let targetData = this.dataForTarget(target);
702         let existingScript = targetData.scriptForIdentifier(scriptIdentifier);
703         if (existingScript) {
704             console.assert(existingScript.url === (url || null));
705             console.assert(existingScript.range.startLine === startLine);
706             console.assert(existingScript.range.startColumn === startColumn);
707             console.assert(existingScript.range.endLine === endLine);
708             console.assert(existingScript.range.endColumn === endColumn);
709             return;
710         }
711
712         if (!WI.isDebugUIEnabled() && isWebKitInternalScript(sourceURL))
713             return;
714
715         let range = new WI.TextRange(startLine, startColumn, endLine, endColumn);
716         let sourceType = isModule ? WI.Script.SourceType.Module : WI.Script.SourceType.Program;
717         let script = new WI.Script(target, scriptIdentifier, range, url, sourceType, isContentScript, sourceURL, sourceMapURL);
718
719         targetData.addScript(script);
720
721         // FIXME: <https://webkit.org/b/164427> Web Inspector: WorkerTarget's mainResource should be a Resource not a Script
722         // We make the main resource of a WorkerTarget the Script instead of the Resource
723         // because the frontend may not be informed of the Resource. We should guarantee
724         // the frontend is informed of the Resource.
725         if (WI.sharedApp.debuggableType === WI.DebuggableType.ServiceWorker) {
726             // A ServiceWorker starts with a LocalScript for the main resource but we can replace it during initialization.
727             if (target.mainResource instanceof WI.LocalScript) {
728                 if (script.url === target.name)
729                     target.mainResource = script;
730             }
731         } else if (!target.mainResource && target !== WI.mainTarget) {
732             // A Worker starts without a main resource and we insert one.
733             if (script.url === target.name) {
734                 target.mainResource = script;
735                 if (script.resource)
736                     target.resourceCollection.remove(script.resource);
737             }
738         }
739
740         if (isWebKitInternalScript(script.sourceURL)) {
741             this._internalWebKitScripts.push(script);
742             if (!WI.isDebugUIEnabled())
743                 return;
744         }
745
746         // Console expressions are not added to the UI by default.
747         if (isWebInspectorConsoleEvaluationScript(script.sourceURL))
748             return;
749
750         this.dispatchEventToListeners(WI.DebuggerManager.Event.ScriptAdded, {script});
751
752         if ((target !== WI.mainTarget || WI.sharedApp.debuggableType === WI.DebuggableType.ServiceWorker) && !script.isMainResource() && !script.resource)
753             target.addScript(script);
754     }
755
756     // Private
757
758     _sourceCodeLocationFromPayload(target, payload)
759     {
760         let targetData = this.dataForTarget(target);
761         let script = targetData.scriptForIdentifier(payload.scriptId);
762         if (!script)
763             return null;
764
765         return script.createSourceCodeLocation(payload.lineNumber, payload.columnNumber);
766     }
767
768     _scopeChainFromPayload(target, payload)
769     {
770         let scopeChain = [];
771         for (let i = 0; i < payload.length; ++i)
772             scopeChain.push(this._scopeChainNodeFromPayload(target, payload[i]));
773         return scopeChain;
774     }
775
776     _scopeChainNodeFromPayload(target, payload)
777     {
778         var type = null;
779         switch (payload.type) {
780         case DebuggerAgent.ScopeType.Global:
781             type = WI.ScopeChainNode.Type.Global;
782             break;
783         case DebuggerAgent.ScopeType.With:
784             type = WI.ScopeChainNode.Type.With;
785             break;
786         case DebuggerAgent.ScopeType.Closure:
787             type = WI.ScopeChainNode.Type.Closure;
788             break;
789         case DebuggerAgent.ScopeType.Catch:
790             type = WI.ScopeChainNode.Type.Catch;
791             break;
792         case DebuggerAgent.ScopeType.FunctionName:
793             type = WI.ScopeChainNode.Type.FunctionName;
794             break;
795         case DebuggerAgent.ScopeType.NestedLexical:
796             type = WI.ScopeChainNode.Type.Block;
797             break;
798         case DebuggerAgent.ScopeType.GlobalLexicalEnvironment:
799             type = WI.ScopeChainNode.Type.GlobalLexicalEnvironment;
800             break;
801
802         // COMPATIBILITY (iOS 9): Debugger.ScopeType.Local used to be provided by the backend.
803         // Newer backends no longer send this enum value, it should be computed by the frontend.
804         // Map this to "Closure" type. The frontend can recalculate this when needed.
805         case DebuggerAgent.ScopeType.Local:
806             type = WI.ScopeChainNode.Type.Closure;
807             break;
808
809         default:
810             console.error("Unknown type: " + payload.type);
811         }
812
813         let object = WI.RemoteObject.fromPayload(payload.object, target);
814         return new WI.ScopeChainNode(type, [object], payload.name, payload.location, payload.empty);
815     }
816
817     _pauseReasonFromPayload(payload)
818     {
819         // FIXME: Handle other backend pause reasons.
820         switch (payload) {
821         case DebuggerAgent.PausedReason.AnimationFrame:
822             return WI.DebuggerManager.PauseReason.AnimationFrame;
823         case DebuggerAgent.PausedReason.Assert:
824             return WI.DebuggerManager.PauseReason.Assertion;
825         case DebuggerAgent.PausedReason.Breakpoint:
826             return WI.DebuggerManager.PauseReason.Breakpoint;
827         case DebuggerAgent.PausedReason.CSPViolation:
828             return WI.DebuggerManager.PauseReason.CSPViolation;
829         case DebuggerAgent.PausedReason.DOM:
830             return WI.DebuggerManager.PauseReason.DOM;
831         case DebuggerAgent.PausedReason.DebuggerStatement:
832             return WI.DebuggerManager.PauseReason.DebuggerStatement;
833         case DebuggerAgent.PausedReason.EventListener:
834             return WI.DebuggerManager.PauseReason.EventListener;
835         case DebuggerAgent.PausedReason.Exception:
836             return WI.DebuggerManager.PauseReason.Exception;
837         case DebuggerAgent.PausedReason.PauseOnNextStatement:
838             return WI.DebuggerManager.PauseReason.PauseOnNextStatement;
839         case DebuggerAgent.PausedReason.Timer:
840             return WI.DebuggerManager.PauseReason.Timer;
841         case DebuggerAgent.PausedReason.XHR:
842             return WI.DebuggerManager.PauseReason.XHR;
843         default:
844             return WI.DebuggerManager.PauseReason.Other;
845         }
846     }
847
848     _debuggerBreakpointActionType(type)
849     {
850         switch (type) {
851         case WI.BreakpointAction.Type.Log:
852             return DebuggerAgent.BreakpointActionType.Log;
853         case WI.BreakpointAction.Type.Evaluate:
854             return DebuggerAgent.BreakpointActionType.Evaluate;
855         case WI.BreakpointAction.Type.Sound:
856             return DebuggerAgent.BreakpointActionType.Sound;
857         case WI.BreakpointAction.Type.Probe:
858             return DebuggerAgent.BreakpointActionType.Probe;
859         default:
860             console.assert(false);
861             return DebuggerAgent.BreakpointActionType.Log;
862         }
863     }
864
865     _debuggerBreakpointOptions(breakpoint)
866     {
867         const templatePlaceholderRegex = /\$\{.*?\}/;
868
869         let options = breakpoint.options;
870         let invalidActions = [];
871
872         for (let action of options.actions) {
873             if (action.type !== WI.BreakpointAction.Type.Log)
874                 continue;
875
876             if (!templatePlaceholderRegex.test(action.data))
877                 continue;
878
879             let lexer = new WI.BreakpointLogMessageLexer;
880             let tokens = lexer.tokenize(action.data);
881             if (!tokens) {
882                 invalidActions.push(action);
883                 continue;
884             }
885
886             let templateLiteral = tokens.reduce((text, token) => {
887                 if (token.type === WI.BreakpointLogMessageLexer.TokenType.PlainText)
888                     return text + token.data.escapeCharacters("`\\");
889                 if (token.type === WI.BreakpointLogMessageLexer.TokenType.Expression)
890                     return text + "${" + token.data + "}";
891                 return text;
892             }, "");
893
894             action.data = "console.log(`" + templateLiteral + "`)";
895             action.type = WI.BreakpointAction.Type.Evaluate;
896         }
897
898         for (let invalidAction of invalidActions)
899             options.actions.remove(invalidAction);
900
901         return options;
902     }
903
904     _setBreakpoint(breakpoint, specificTarget, callback)
905     {
906         console.assert(!breakpoint.disabled);
907
908         if (breakpoint.disabled)
909             return;
910
911         if (!this._restoringBreakpoints && !this.breakpointsDisabledTemporarily) {
912             // Enable breakpoints since a breakpoint is being set. This eliminates
913             // a multi-step process for the user that can be confusing.
914             this.breakpointsEnabled = true;
915         }
916
917         function didSetBreakpoint(target, error, breakpointIdentifier, locations)
918         {
919             if (error)
920                 return;
921
922             this._breakpointIdMap.set(breakpointIdentifier, breakpoint);
923
924             breakpoint.identifier = breakpointIdentifier;
925
926             // Debugger.setBreakpoint returns a single location.
927             if (!(locations instanceof Array))
928                 locations = [locations];
929
930             for (let location of locations)
931                 this.breakpointResolved(target, breakpointIdentifier, location);
932
933             if (typeof callback === "function")
934                 callback();
935         }
936
937         // The breakpoint will be resolved again by calling DebuggerAgent, so mark it as unresolved.
938         // If something goes wrong it will stay unresolved and show up as such in the user interface.
939         // When setting for a new target, don't change the resolved target.
940         if (!specificTarget)
941             breakpoint.resolved = false;
942
943         // Convert BreakpointAction types to DebuggerAgent protocol types.
944         // NOTE: Breakpoint.options returns new objects each time, so it is safe to modify.
945         let options = this._debuggerBreakpointOptions(breakpoint);
946         if (options.actions.length) {
947             for (let action of options.actions)
948                 action.type = this._debuggerBreakpointActionType(action.type);
949         }
950
951         if (breakpoint.contentIdentifier) {
952             let targets = specificTarget ? [specificTarget] : WI.targets;
953             for (let target of targets) {
954                 target.DebuggerAgent.setBreakpointByUrl.invoke({
955                     lineNumber: breakpoint.sourceCodeLocation.lineNumber,
956                     url: breakpoint.contentIdentifier,
957                     urlRegex: undefined,
958                     columnNumber: breakpoint.sourceCodeLocation.columnNumber,
959                     options
960                 }, didSetBreakpoint.bind(this, target), target.DebuggerAgent);
961             }
962         } else if (breakpoint.scriptIdentifier) {
963             let target = breakpoint.target;
964             target.DebuggerAgent.setBreakpoint.invoke({
965                 location: {scriptId: breakpoint.scriptIdentifier, lineNumber: breakpoint.sourceCodeLocation.lineNumber, columnNumber: breakpoint.sourceCodeLocation.columnNumber},
966                 options
967             }, didSetBreakpoint.bind(this, target), target.DebuggerAgent);
968         }
969     }
970
971     _removeBreakpoint(breakpoint, callback)
972     {
973         if (!breakpoint.identifier)
974             return;
975
976         function didRemoveBreakpoint(error)
977         {
978             if (error)
979                 console.error(error);
980
981             this._breakpointIdMap.delete(breakpoint.identifier);
982
983             breakpoint.identifier = null;
984
985             // Don't reset resolved here since we want to keep disabled breakpoints looking like they
986             // are resolved in the user interface. They will get marked as unresolved in reset.
987
988             if (typeof callback === "function")
989                 callback();
990         }
991
992         if (breakpoint.contentIdentifier) {
993             for (let target of WI.targets)
994                 target.DebuggerAgent.removeBreakpoint(breakpoint.identifier, didRemoveBreakpoint.bind(this));
995         } else if (breakpoint.scriptIdentifier) {
996             let target = breakpoint.target;
997             target.DebuggerAgent.removeBreakpoint(breakpoint.identifier, didRemoveBreakpoint.bind(this));
998         }
999     }
1000
1001     _breakpointDisplayLocationDidChange(event)
1002     {
1003         if (this._ignoreBreakpointDisplayLocationDidChangeEvent)
1004             return;
1005
1006         let breakpoint = event.target;
1007         if (!breakpoint.identifier || breakpoint.disabled)
1008             return;
1009
1010         // Remove the breakpoint with its old id.
1011         this._removeBreakpoint(breakpoint, breakpointRemoved.bind(this));
1012
1013         function breakpointRemoved()
1014         {
1015             // Add the breakpoint at its new lineNumber and get a new id.
1016             this._setBreakpoint(breakpoint);
1017
1018             this.dispatchEventToListeners(WI.DebuggerManager.Event.BreakpointMoved, {breakpoint});
1019         }
1020     }
1021
1022     _breakpointDisabledStateDidChange(event)
1023     {
1024         this._saveBreakpoints();
1025
1026         let breakpoint = event.target;
1027         if (breakpoint === this._allExceptionsBreakpoint) {
1028             if (!breakpoint.disabled && !this.breakpointsDisabledTemporarily)
1029                 this.breakpointsEnabled = true;
1030             this._allExceptionsBreakpointEnabledSetting.value = !breakpoint.disabled;
1031             this._updateBreakOnExceptionsState();
1032             for (let target of WI.targets)
1033                 target.DebuggerAgent.setPauseOnExceptions(this._breakOnExceptionsState);
1034             return;
1035         }
1036
1037         if (breakpoint === this._allUncaughtExceptionsBreakpoint) {
1038             if (!breakpoint.disabled && !this.breakpointsDisabledTemporarily)
1039                 this.breakpointsEnabled = true;
1040             this._allUncaughtExceptionsBreakpointEnabledSetting.value = !breakpoint.disabled;
1041             this._updateBreakOnExceptionsState();
1042             for (let target of WI.targets)
1043                 target.DebuggerAgent.setPauseOnExceptions(this._breakOnExceptionsState);
1044             return;
1045         }
1046
1047         if (breakpoint === this._assertionsBreakpoint) {
1048             if (!breakpoint.disabled && !this.breakpointsDisabledTemporarily)
1049                 this.breakpointsEnabled = true;
1050             this._assertionsBreakpointEnabledSetting.value = !breakpoint.disabled;
1051             for (let target of WI.targets)
1052                 target.DebuggerAgent.setPauseOnAssertions(this._assertionsBreakpointEnabledSetting.value);
1053             return;
1054         }
1055
1056         if (breakpoint.disabled)
1057             this._removeBreakpoint(breakpoint);
1058         else
1059             this._setBreakpoint(breakpoint);
1060     }
1061
1062     _breakpointEditablePropertyDidChange(event)
1063     {
1064         this._saveBreakpoints();
1065
1066         let breakpoint = event.target;
1067         if (breakpoint.disabled)
1068             return;
1069
1070         console.assert(this.isBreakpointEditable(breakpoint));
1071         if (!this.isBreakpointEditable(breakpoint))
1072             return;
1073
1074         // Remove the breakpoint with its old id.
1075         this._removeBreakpoint(breakpoint, breakpointRemoved.bind(this));
1076
1077         function breakpointRemoved()
1078         {
1079             // Add the breakpoint with its new properties and get a new id.
1080             this._setBreakpoint(breakpoint);
1081         }
1082     }
1083
1084     _startDisablingBreakpointsTemporarily()
1085     {
1086         console.assert(!this.breakpointsDisabledTemporarily, "Already temporarily disabling breakpoints.");
1087         if (this.breakpointsDisabledTemporarily)
1088             return;
1089
1090         this._temporarilyDisabledBreakpointsRestoreSetting.value = this._breakpointsEnabledSetting.value;
1091
1092         this.breakpointsEnabled = false;
1093     }
1094
1095     _stopDisablingBreakpointsTemporarily()
1096     {
1097         console.assert(this.breakpointsDisabledTemporarily, "Was not temporarily disabling breakpoints.");
1098         if (!this.breakpointsDisabledTemporarily)
1099             return;
1100
1101         let restoreState = this._temporarilyDisabledBreakpointsRestoreSetting.value;
1102         this._temporarilyDisabledBreakpointsRestoreSetting.value = null;
1103
1104         this.breakpointsEnabled = restoreState;
1105     }
1106
1107     _timelineCapturingWillStart(event)
1108     {
1109         this._startDisablingBreakpointsTemporarily();
1110
1111         if (this.paused)
1112             this.resume();
1113     }
1114
1115     _timelineCapturingStopped(event)
1116     {
1117         this._stopDisablingBreakpointsTemporarily();
1118     }
1119
1120     _targetRemoved(event)
1121     {
1122         let wasPaused = this.paused;
1123
1124         this._targetDebuggerDataMap.delete(event.data.target);
1125
1126         if (!this.paused && wasPaused)
1127             this.dispatchEventToListeners(WI.DebuggerManager.Event.Resumed);
1128     }
1129
1130     _mainResourceDidChange(event)
1131     {
1132         if (!event.target.isMainFrame())
1133             return;
1134
1135         this._didResumeInternal(WI.mainTarget);
1136     }
1137
1138     _didResumeInternal(target)
1139     {
1140         if (!this.paused)
1141             return;
1142
1143         if (this._delayedResumeTimeout) {
1144             clearTimeout(this._delayedResumeTimeout);
1145             this._delayedResumeTimeout = undefined;
1146         }
1147
1148         let activeCallFrameDidChange = false;
1149         if (this._activeCallFrame && this._activeCallFrame.target === target) {
1150             this._activeCallFrame = null;
1151             activeCallFrameDidChange = true;
1152         }
1153
1154         this.dataForTarget(target).updateForResume();
1155
1156         if (!this.paused)
1157             this.dispatchEventToListeners(WI.DebuggerManager.Event.Resumed);
1158
1159         this.dispatchEventToListeners(WI.DebuggerManager.Event.CallFramesDidChange, {target});
1160
1161         if (activeCallFrameDidChange)
1162             this.dispatchEventToListeners(WI.DebuggerManager.Event.ActiveCallFrameDidChange);
1163     }
1164
1165     _updateBreakOnExceptionsState()
1166     {
1167         let state = "none";
1168
1169         if (this._breakpointsEnabledSetting.value) {
1170             if (!this._allExceptionsBreakpoint.disabled)
1171                 state = "all";
1172             else if (!this._allUncaughtExceptionsBreakpoint.disabled)
1173                 state = "uncaught";
1174         }
1175
1176         this._breakOnExceptionsState = state;
1177
1178         switch (state) {
1179         case "all":
1180             // Mark the uncaught breakpoint as unresolved since "all" includes "uncaught".
1181             // That way it is clear in the user interface that the breakpoint is ignored.
1182             this._allUncaughtExceptionsBreakpoint.resolved = false;
1183             break;
1184         case "uncaught":
1185         case "none":
1186             // Mark the uncaught breakpoint as resolved again.
1187             this._allUncaughtExceptionsBreakpoint.resolved = true;
1188             break;
1189         }
1190     }
1191
1192     _saveBreakpoints()
1193     {
1194         if (this._restoringBreakpoints)
1195             return;
1196
1197         let breakpointsToSave = this._breakpoints.filter((breakpoint) => !!breakpoint.contentIdentifier);
1198         let serializedBreakpoints = breakpointsToSave.map((breakpoint) => breakpoint.info);
1199         this._breakpointsSetting.value = serializedBreakpoints;
1200     }
1201
1202     _associateBreakpointsWithSourceCode(breakpoints, sourceCode)
1203     {
1204         this._ignoreBreakpointDisplayLocationDidChangeEvent = true;
1205
1206         for (let breakpoint of breakpoints) {
1207             if (!breakpoint.sourceCodeLocation.sourceCode)
1208                 breakpoint.sourceCodeLocation.sourceCode = sourceCode;
1209             // SourceCodes can be unequal if the SourceCodeLocation is associated with a Script and we are looking at the Resource.
1210             console.assert(breakpoint.sourceCodeLocation.sourceCode === sourceCode || breakpoint.sourceCodeLocation.sourceCode.contentIdentifier === sourceCode.contentIdentifier);
1211         }
1212
1213         this._ignoreBreakpointDisplayLocationDidChangeEvent = false;
1214     }
1215
1216     _debugUIEnabledDidChange()
1217     {
1218         let eventType = WI.isDebugUIEnabled() ? WI.DebuggerManager.Event.ScriptAdded : WI.DebuggerManager.Event.ScriptRemoved;
1219         for (let script of this._internalWebKitScripts)
1220             this.dispatchEventToListeners(eventType, {script});
1221     }
1222 };
1223
1224 WI.DebuggerManager.Event = {
1225     BreakpointAdded: "debugger-manager-breakpoint-added",
1226     BreakpointRemoved: "debugger-manager-breakpoint-removed",
1227     BreakpointMoved: "debugger-manager-breakpoint-moved",
1228     WaitingToPause: "debugger-manager-waiting-to-pause",
1229     Paused: "debugger-manager-paused",
1230     Resumed: "debugger-manager-resumed",
1231     CallFramesDidChange: "debugger-manager-call-frames-did-change",
1232     ActiveCallFrameDidChange: "debugger-manager-active-call-frame-did-change",
1233     ScriptAdded: "debugger-manager-script-added",
1234     ScriptRemoved: "debugger-manager-script-removed",
1235     ScriptsCleared: "debugger-manager-scripts-cleared",
1236     BreakpointsEnabledDidChange: "debugger-manager-breakpoints-enabled-did-change"
1237 };
1238
1239 WI.DebuggerManager.PauseReason = {
1240     AnimationFrame: "animation-frame",
1241     Assertion: "assertion",
1242     Breakpoint: "breakpoint",
1243     CSPViolation: "CSP-violation",
1244     DebuggerStatement: "debugger-statement",
1245     DOM: "DOM",
1246     EventListener: "event-listener",
1247     Exception: "exception",
1248     PauseOnNextStatement: "pause-on-next-statement",
1249     Timer: "timer",
1250     XHR: "xhr",
1251     Other: "other",
1252 };