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