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