Web Inspector: Using Breakpoint Actions Breaks iOS inspection
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / DebuggerManager.js
1 /*
2  * Copyright (C) 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 WebInspector.DebuggerManager = function()
27 {
28     WebInspector.Object.call(this);
29
30     DebuggerAgent.enable();
31
32     WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.DisplayLocationDidChange, this._breakpointDisplayLocationDidChange, this);
33     WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.DisabledStateDidChange, this._breakpointDisabledStateDidChange, this);
34     WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.ConditionDidChange, this._breakpointEditablePropertyDidChange, this);
35     WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.AutoContinueDidChange, this._breakpointEditablePropertyDidChange, this);
36     WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.ActionsDidChange, this._breakpointEditablePropertyDidChange, this);
37
38     window.addEventListener("pagehide", this._inspectorClosing.bind(this));
39
40     this._allExceptionsBreakpointEnabledSetting = new WebInspector.Setting("break-on-all-exceptions", false);
41     this._allUncaughtExceptionsBreakpointEnabledSetting = new WebInspector.Setting("break-on-all-uncaught-exceptions", false);
42
43     var specialBreakpointLocation = new WebInspector.SourceCodeLocation(null, Infinity, Infinity);
44
45     this._allExceptionsBreakpoint = new WebInspector.Breakpoint(specialBreakpointLocation, !this._allExceptionsBreakpointEnabledSetting.value);
46     this._allExceptionsBreakpoint.resolved = true;
47
48     this._allUncaughtExceptionsBreakpoint = new WebInspector.Breakpoint(specialBreakpointLocation, !this._allUncaughtExceptionsBreakpointEnabledSetting.value);
49
50     this._breakpoints = [];
51     this._breakpointURLMap = {};
52     this._breakpointScriptIdentifierMap = {};
53     this._breakpointIdMap = {};
54
55     this._scriptIdMap = {};
56     this._scriptURLMap = {};
57
58     this._breakpointsSetting = new WebInspector.Setting("breakpoints", []);
59     this._breakpointsEnabledSetting = new WebInspector.Setting("breakpoints-enabled", true);
60
61     DebuggerAgent.setBreakpointsActive(this._breakpointsEnabledSetting.value);
62
63     this._updateBreakOnExceptionsState();
64
65     var savedBreakpoints = this._breakpointsSetting.value;
66     for (var i = 0; i < savedBreakpoints.length; ++i) {
67         var breakpoint = new WebInspector.Breakpoint(savedBreakpoints[i]);
68         this.addBreakpoint(breakpoint, true);
69     }
70 };
71
72 WebInspector.DebuggerManager.Event = {
73     BreakpointAdded: "debugger-manager-breakpoint-added",
74     BreakpointRemoved: "debugger-manager-breakpoint-removed",
75     BreakpointMoved: "debugger-manager-breakpoint-moved",
76     Paused: "debugger-manager-paused",
77     Resumed: "debugger-manager-resumed",
78     CallFramesDidChange: "debugger-manager-call-frames-did-change",
79     ActiveCallFrameDidChange: "debugger-manager-active-call-frame-did-change",
80     ScriptAdded: "debugger-manager-script-added",
81     ScriptsCleared: "debugger-manager-scripts-cleared"
82 };
83
84 WebInspector.DebuggerManager.prototype = {
85     constructor: WebInspector.DebuggerManager,
86
87     // Public
88
89     get breakpointsEnabled()
90     {
91         return this._breakpointsEnabledSetting.value;
92     },
93
94     set breakpointsEnabled(enabled)
95     {
96         if (this._breakpointsEnabled === enabled)
97             return;
98
99         this._breakpointsEnabledSetting.value = enabled;
100
101         this._allExceptionsBreakpoint.dispatchEventToListeners(WebInspector.Breakpoint.Event.ResolvedStateDidChange);
102         this._allUncaughtExceptionsBreakpoint.dispatchEventToListeners(WebInspector.Breakpoint.Event.ResolvedStateDidChange);
103
104         for (var i = 0; i < this._breakpoints.length; ++i)
105             this._breakpoints[i].dispatchEventToListeners(WebInspector.Breakpoint.Event.ResolvedStateDidChange);
106
107         DebuggerAgent.setBreakpointsActive(enabled);
108
109         this._updateBreakOnExceptionsState();
110     },
111
112     get paused()
113     {
114         return this._paused;
115     },
116
117     get callFrames()
118     {
119         return this._callFrames;
120     },
121
122     get activeCallFrame()
123     {
124         return this._activeCallFrame;
125     },
126
127     set activeCallFrame(callFrame)
128     {
129         if (callFrame === this._activeCallFrame)
130             return;
131
132         this._activeCallFrame = callFrame || null;
133
134         this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.ActiveCallFrameDidChange);
135     },
136
137     pause: function()
138     {
139         DebuggerAgent.pause();
140     },
141
142     resume: function()
143     {
144         DebuggerAgent.resume();
145     },
146
147     stepOver: function()
148     {
149         DebuggerAgent.stepOver();
150     },
151
152     stepInto: function()
153     {
154         DebuggerAgent.stepInto();
155     },
156
157     stepOut: function()
158     {
159         DebuggerAgent.stepOut();
160     },
161
162     get allExceptionsBreakpoint()
163     {
164         return this._allExceptionsBreakpoint;
165     },
166
167     get allUncaughtExceptionsBreakpoint()
168     {
169         return this._allUncaughtExceptionsBreakpoint;
170     },
171
172     get breakpoints()
173     {
174         return this._breakpoints;
175     },
176
177     breakpointsForSourceCode: function(sourceCode)
178     {
179         console.assert(sourceCode instanceof WebInspector.Resource || sourceCode instanceof WebInspector.Script);
180
181         if (sourceCode instanceof WebInspector.SourceMapResource) {
182             var mappedResourceBreakpoints = [];
183             var originalSourceCodeBreakpoints = this.breakpointsForSourceCode(sourceCode.sourceMap.originalSourceCode);
184             return originalSourceCodeBreakpoints.filter(function(breakpoint) {
185                 return breakpoint.sourceCodeLocation.displaySourceCode === sourceCode;
186             });
187         }
188
189         if (sourceCode.url in this._breakpointURLMap) {
190             var urlBreakpoint = this._breakpointURLMap[sourceCode.url] || [];
191             this._associateBreakpointsWithSourceCode(urlBreakpoint, sourceCode);
192             return urlBreakpoint;
193         }
194
195         if (sourceCode instanceof WebInspector.Script && sourceCode.id in this._breakpointScriptIdentifierMap) {
196             var scriptIdentifierBreakpoints = this._breakpointScriptIdentifierMap[sourceCode.id] || [];
197             this._associateBreakpointsWithSourceCode(scriptIdentifierBreakpoints, sourceCode);
198             return scriptIdentifierBreakpoints;
199         }
200
201         return [];
202     },
203
204     scriptForIdentifier: function(id)
205     {
206         return this._scriptIdMap[id] || null;
207     },
208
209     scriptsForURL: function(url)
210     {
211         // FIXME: This may not be safe. A Resource's URL may differ from a Script's URL.
212         return this._scriptURLMap[url] || [];
213     },
214
215     continueToLocation: function(scriptIdentifier, lineNumber, columnNumber)
216     {
217         DebuggerAgent.continueToLocation({scriptId: scriptIdentifier, lineNumber: lineNumber, columnNumber: columnNumber});
218     },
219
220     addBreakpoint: function(breakpoint, skipEventDispatch)
221     {
222         console.assert(breakpoint);
223         if (!breakpoint)
224             return;
225
226         if (breakpoint.url) {
227             var urlBreakpoints = this._breakpointURLMap[breakpoint.url];
228             if (!urlBreakpoints)
229                 urlBreakpoints = this._breakpointURLMap[breakpoint.url] = [];
230             urlBreakpoints.push(breakpoint);
231         }
232
233         if (breakpoint.scriptIdentifier) {
234             var scriptIdentifierBreakpoints = this._breakpointScriptIdentifierMap[breakpoint.scriptIdentifier];
235             if (!scriptIdentifierBreakpoints)
236                 scriptIdentifierBreakpoints = this._breakpointScriptIdentifierMap[breakpoint.scriptIdentifier] = [];
237             scriptIdentifierBreakpoints.push(breakpoint);
238         }
239
240         this._breakpoints.push(breakpoint);
241
242         if (!breakpoint.disabled)
243             this._setBreakpoint(breakpoint);
244
245         if (!skipEventDispatch)
246             this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.BreakpointAdded, {breakpoint: breakpoint});
247     },
248
249     removeBreakpoint: function(breakpoint)
250     {
251         console.assert(breakpoint);
252         if (!breakpoint)
253             return;
254
255         console.assert(this.isBreakpointRemovable(breakpoint));
256         if (!this.isBreakpointRemovable(breakpoint))
257             return;
258
259         this._breakpoints.remove(breakpoint);
260
261         if (breakpoint.id)
262             this._removeBreakpoint(breakpoint);
263
264         if (breakpoint.url) {
265             var urlBreakpoints = this._breakpointURLMap[breakpoint.url];
266             if (urlBreakpoints) {
267                 urlBreakpoints.remove(breakpoint);
268                 if (!urlBreakpoints.length)
269                     delete this._breakpointURLMap[breakpoint.url];
270             }
271         }
272
273         if (breakpoint.scriptIdentifier) {
274             var scriptIdentifierBreakpoints = this._breakpointScriptIdentifierMap[breakpoint.scriptIdentifier];
275             if (scriptIdentifierBreakpoints) {
276                 scriptIdentifierBreakpoints.remove(breakpoint);
277                 if (!scriptIdentifierBreakpoints.length)
278                     delete this._breakpointScriptIdentifierMap[breakpoint.scriptIdentifier];
279             }
280         }
281
282         this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.BreakpointRemoved, {breakpoint: breakpoint});
283     },
284
285     breakpointResolved: function(breakpointIdentifier, location)
286     {
287         // Called from WebInspector.DebuggerObserver.
288
289         var breakpoint = this._breakpointIdMap[breakpointIdentifier];
290         console.assert(breakpoint);
291         if (!breakpoint)
292             return;
293
294         console.assert(breakpoint.id === breakpointIdentifier);
295
296         breakpoint.resolved = true;
297     },
298
299     reset: function()
300     {
301         // Called from WebInspector.DebuggerObserver.
302
303         var wasPaused = this._paused;
304
305         WebInspector.Script.resetUniqueDisplayNameNumbers();
306
307         this._paused = false;
308         this._scriptIdMap = {};
309         this._scriptURLMap = {};
310
311         this._ignoreBreakpointDisplayLocationDidChangeEvent = true;
312
313         // Mark all the breakpoints as unresolved. They will be reported as resolved when
314         // breakpointResolved is called as the page loads.
315         for (var i = 0; i < this._breakpoints.length; ++i) {
316             var breakpoint = this._breakpoints[i];
317             breakpoint.resolved = false;
318             if (breakpoint.sourceCodeLocation.sourceCode)
319                 breakpoint.sourceCodeLocation.sourceCode = null;
320         }
321
322         delete this._ignoreBreakpointDisplayLocationDidChangeEvent;
323
324         this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.ScriptsCleared);
325
326         if (wasPaused)
327             this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.Resumed);
328     },
329
330     debuggerDidPause: function(callFramesPayload)
331     {
332         // Called from WebInspector.DebuggerObserver.
333
334         if (this._delayedResumeTimeout) {
335             clearTimeout(this._delayedResumeTimeout);
336             delete this._delayedResumeTimeout;
337         }
338
339         var wasStillPaused = this._paused;
340
341         this._paused = true;
342         this._callFrames = [];
343
344         for (var i = 0; i < callFramesPayload.length; ++i) {
345             var callFramePayload = callFramesPayload[i];
346             var sourceCodeLocation = this._sourceCodeLocationFromPayload(callFramePayload.location);
347             // Exclude the case where the call frame is in the inspector code.
348             if (!sourceCodeLocation || sourceCodeLocation._sourceCode._url.indexOf("__WebInspector") === 0)
349                 continue;
350             var thisObject = WebInspector.RemoteObject.fromPayload(callFramePayload.this);
351             var scopeChain = this._scopeChainFromPayload(callFramePayload.scopeChain);
352             var callFrame = new WebInspector.CallFrame(callFramePayload.callFrameId, sourceCodeLocation, callFramePayload.functionName, thisObject, scopeChain);
353             this._callFrames.push(callFrame);
354         }
355
356         if (!this._callFrames.length) {
357             this.resume();
358             return;
359         }
360
361         this._activeCallFrame = this._callFrames[0];
362
363         if (!wasStillPaused)
364             this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.Paused);
365         this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.CallFramesDidChange);
366         this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.ActiveCallFrameDidChange);
367     },
368
369     debuggerDidResume: function()
370     {
371         // Called from WebInspector.DebuggerObserver.
372
373         function delayedWork()
374         {
375             delete this._delayedResumeTimeout;
376
377             this._paused = false;
378             this._callFrames = null;
379             this._activeCallFrame = null;
380
381             this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.Resumed);
382             this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.CallFramesDidChange);
383             this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.ActiveCallFrameDidChange);
384         }
385
386         // We delay clearing the state and firing events so the user interface does not flash
387         // between brief steps or successive breakpoints.
388         this._delayedResumeTimeout = setTimeout(delayedWork.bind(this), 50);
389     },
390
391     scriptDidParse: function(scriptIdentifier, url, isContentScript, startLine, startColumn, endLine, endColumn, sourceMapURL)
392     {
393         // Don't add the script again if it is already known.
394         if (this._scriptIdMap[scriptIdentifier]) {
395             console.assert(this._scriptIdMap[scriptIdentifier].url === url);
396             console.assert(this._scriptIdMap[scriptIdentifier].range.startLine === startLine);
397             console.assert(this._scriptIdMap[scriptIdentifier].range.startColumn === startColumn);
398             console.assert(this._scriptIdMap[scriptIdentifier].range.endLine === endLine);
399             console.assert(this._scriptIdMap[scriptIdentifier].range.endColumn === endColumn);
400             return;
401         }
402
403         var script = new WebInspector.Script(scriptIdentifier, new WebInspector.TextRange(startLine, startColumn, endLine, endColumn), url, isContentScript, sourceMapURL);
404
405         this._scriptIdMap[scriptIdentifier] = script;
406
407         if (script.url) {
408             var scripts = this._scriptURLMap[script.url];
409             if (!scripts)
410                 scripts = this._scriptURLMap[script.url] = [];
411             scripts.push(script);
412         }
413
414         this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.ScriptAdded, {script: script});
415     },
416
417     isBreakpointRemovable: function(breakpoint)
418     {
419         return breakpoint !== this._allExceptionsBreakpoint && breakpoint !== this._allUncaughtExceptionsBreakpoint;
420     },
421
422     isBreakpointEditable: function(breakpoint)
423     {
424         return this.isBreakpointRemovable(breakpoint);
425     },
426
427     // Private
428
429     _sourceCodeLocationFromPayload: function(payload)
430     {
431         var script = this._scriptIdMap[payload.scriptId];
432         console.assert(script);
433         if (!script)
434             return null;
435
436         return script.createSourceCodeLocation(payload.lineNumber, payload.columnNumber);
437     },
438
439     _scopeChainFromPayload: function(payload)
440     {
441         var scopeChain = [];
442         for (var i = 0; i < payload.length; ++i)
443             scopeChain.push(this._scopeChainNodeFromPayload(payload[i]));
444         return scopeChain;
445     },
446
447     _scopeChainNodeFromPayload: function(payload)
448     {
449         var type = null;
450         switch (payload.type) {
451         case "local":
452             type = WebInspector.ScopeChainNode.Type.Local;
453             break;
454         case "global":
455             type = WebInspector.ScopeChainNode.Type.Global;
456             break;
457         case "with":
458             type = WebInspector.ScopeChainNode.Type.With;
459             break;
460         case "closure":
461             type = WebInspector.ScopeChainNode.Type.Closure;
462             break;
463         case "catch":
464             type = WebInspector.ScopeChainNode.Type.Catch;
465             break;
466         default:
467             console.error("Unknown type: " + payload.type);
468         }
469
470         var object = WebInspector.RemoteObject.fromPayload(payload.object);
471         return new WebInspector.ScopeChainNode(type, object);
472     },
473
474     _debuggerBreakpointActionType: function(type)
475     {
476         switch (type) {
477         case WebInspector.BreakpointAction.Type.Log:
478             return DebuggerAgent.BreakpointActionType.Log;
479         case WebInspector.BreakpointAction.Type.Evaluate:
480             return DebuggerAgent.BreakpointActionType.Evaluate;
481         case WebInspector.BreakpointAction.Type.Sound:
482             return DebuggerAgent.BreakpointActionType.Sound;
483         default:
484             console.assert(false);
485             return DebuggerAgent.BreakpointActionType.Log;
486         }
487     },
488
489     _setBreakpoint: function(breakpoint, callback)
490     {
491         console.assert(!breakpoint.id);
492         console.assert(!breakpoint.disabled);
493
494         if (breakpoint.id || breakpoint.disabled)
495             return;
496
497         function didSetBreakpoint(error, breakpointIdentifier)
498         {
499             if (error)
500                 return;
501
502             this._breakpointIdMap[breakpointIdentifier] = breakpoint;
503
504             breakpoint.id = breakpointIdentifier;
505             breakpoint.resolved = true;
506
507             if (typeof callback === "function")
508                 callback();
509         }
510
511         // The breakpoint will be resolved again by calling DebuggerAgent, so mark it as unresolved.
512         // If something goes wrong it will stay unresolved and show up as such in the user interface.
513         breakpoint.resolved = false;
514
515         // Convert BreakpointAction types to DebuggerAgent protocol types.
516         // NOTE: Breakpoint.options returns new objects each time, so it is safe to modify.
517         var options;
518         if (DebuggerAgent.BreakpointActionType) {
519             options = breakpoint.options;
520             if (options.actions.length) {
521                 for (var i = 0; i < options.actions.length; ++i)
522                     options.actions[i].type = this._debuggerBreakpointActionType(options.actions[i].type);
523             }
524         }
525
526         // COMPATIBILITY (iOS 7): iOS 7 and earlier, DebuggerAgent.setBreakpoint* took a "condition" string argument.
527         // This has been replaced with an "options" BreakpointOptions object.
528         if (breakpoint.url) {
529             DebuggerAgent.setBreakpointByUrl.invoke({
530                 lineNumber: breakpoint.sourceCodeLocation.lineNumber,
531                 url: breakpoint.url,
532                 urlRegex: undefined,
533                 columnNumber: breakpoint.sourceCodeLocation.columnNumber,
534                 condition: breakpoint.condition,
535                 options: options
536             }, didSetBreakpoint.bind(this));
537         } else if (breakpoint.scriptIdentifier) {
538             DebuggerAgent.setBreakpoint.invoke({
539                 location: {scriptId: breakpoint.scriptIdentifier, lineNumber: breakpoint.sourceCodeLocation.lineNumber, columnNumber: breakpoint.sourceCodeLocation.columnNumber},
540                 condition: breakpoint.condition,
541                 options: options
542             }, didSetBreakpoint.bind(this));
543         }
544     },
545
546     _removeBreakpoint: function(breakpoint, callback)
547     {
548         console.assert(breakpoint.id);
549
550         if (!breakpoint.id)
551             return;
552
553         function didRemoveBreakpoint(error)
554         {
555             if (error)
556                 console.error(error);
557
558             delete this._breakpointIdMap[breakpoint.id];
559
560             breakpoint.id = null;
561
562             // Don't reset resolved here since we want to keep disabled breakpoints looking like they
563             // are resolved in the user interface. They will get marked as unresolved in reset.
564
565             if (typeof callback === "function")
566                 callback();
567         }
568
569         DebuggerAgent.removeBreakpoint(breakpoint.id, didRemoveBreakpoint.bind(this));
570     },
571
572     _breakpointDisplayLocationDidChange: function(event)
573     {
574         if (this._ignoreBreakpointDisplayLocationDidChangeEvent)
575             return;
576
577         var breakpoint = event.target;
578         if (!breakpoint.id || breakpoint.disabled)
579             return;
580
581         // Remove the breakpoint with its old id.
582         this._removeBreakpoint(breakpoint, breakpointRemoved.bind(this));
583
584         function breakpointRemoved()
585         {
586             // Add the breakpoint at its new lineNumber and get a new id.
587             this._setBreakpoint(breakpoint);
588
589             this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.BreakpointMoved, {breakpoint: breakpoint});
590         }
591     },
592
593     _breakpointDisabledStateDidChange: function(event)
594     {
595         var breakpoint = event.target;
596
597         if (breakpoint === this._allExceptionsBreakpoint) {
598             this._allExceptionsBreakpointEnabledSetting.value = !breakpoint.disabled;
599             this._updateBreakOnExceptionsState();
600             return;
601         }
602
603         if (breakpoint === this._allUncaughtExceptionsBreakpoint) {
604             this._allUncaughtExceptionsBreakpointEnabledSetting.value = !breakpoint.disabled;
605             this._updateBreakOnExceptionsState();
606             return;
607         }
608
609         if (breakpoint.disabled)
610             this._removeBreakpoint(breakpoint);
611         else
612             this._setBreakpoint(breakpoint);
613     },
614
615     _breakpointEditablePropertyDidChange: function(event)
616     {
617         var breakpoint = event.target;
618         if (breakpoint.disabled)
619             return;
620
621         console.assert(this.isBreakpointEditable(breakpoint));
622         if (!this.isBreakpointEditable(breakpoint))
623             return;
624
625         // Remove the breakpoint with its old id.
626         this._removeBreakpoint(breakpoint, breakpointRemoved.bind(this));
627
628         function breakpointRemoved()
629         {
630             // Add the breakpoint with its new condition and get a new id.
631             this._setBreakpoint(breakpoint);
632         }
633     },
634
635     _updateBreakOnExceptionsState: function()
636     {
637         var state = "none";
638
639         if (this._breakpointsEnabledSetting.value) {
640             if (!this._allExceptionsBreakpoint.disabled)
641                 state = "all";
642             else if (!this._allUncaughtExceptionsBreakpoint.disabled)
643                 state = "uncaught";
644         }
645
646         switch (state) {
647         case "all":
648             // Mark the uncaught breakpoint as unresolved since "all" includes "uncaught".
649             // That way it is clear in the user interface that the breakpoint is ignored.
650             this._allUncaughtExceptionsBreakpoint.resolved = false;
651             break;
652         case "uncaught":
653         case "none":
654             // Mark the uncaught breakpoint as resolved again.
655             this._allUncaughtExceptionsBreakpoint.resolved = true;
656             break;
657         }
658
659         DebuggerAgent.setPauseOnExceptions(state);
660     },
661
662     _inspectorClosing: function(event)
663     {
664         this._saveBreakpoints();
665     },
666
667     _saveBreakpoints: function()
668     {
669         var savedBreakpoints = [];
670
671         for (var i = 0; i < this._breakpoints.length; ++i) {
672             var breakpoint = this._breakpoints[i];
673
674             // Only breakpoints with URLs can be saved. Breakpoints for transient scripts can't.
675             if (!breakpoint.url)
676                 continue;
677
678             savedBreakpoints.push(breakpoint.info);
679         }
680
681         this._breakpointsSetting.value = savedBreakpoints;
682     },
683
684     _associateBreakpointsWithSourceCode: function(breakpoints, sourceCode)
685     {
686         this._ignoreBreakpointDisplayLocationDidChangeEvent = true;
687
688         for (var i = 0; i < breakpoints.length; ++i) {
689             var breakpoint = breakpoints[i];
690             if (breakpoint.sourceCodeLocation.sourceCode === null)
691                 breakpoint.sourceCodeLocation.sourceCode = sourceCode;
692             // SourceCodes can be unequal if the SourceCodeLocation is associated with a Script and we are looking at the Resource.
693             console.assert(breakpoint.sourceCodeLocation.sourceCode === sourceCode || breakpoint.sourceCodeLocation.sourceCode.url === sourceCode.url);
694         }
695
696         delete this._ignoreBreakpointDisplayLocationDidChangeEvent;
697     }
698 };
699
700 WebInspector.DebuggerManager.prototype.__proto__ = WebInspector.Object.prototype;