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