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