Web Inspector: Command-Enter in Debugger should show a popover with evaluation result
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / SourceCodeTextEditor.js
1 /*
2  * Copyright (C) 2013, 2015 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.SourceCodeTextEditor = class SourceCodeTextEditor extends WebInspector.TextEditor
27 {
28     constructor(sourceCode)
29     {
30         console.assert(sourceCode instanceof WebInspector.SourceCode);
31
32         super();
33
34         this.delegate = this;
35
36         this._sourceCode = sourceCode;
37         this._breakpointMap = {};
38         this._issuesLineNumberMap = new Map;
39         this._widgetMap = new Map;
40         this._contentPopulated = false;
41         this._invalidLineNumbers = {0: true};
42         this._ignoreContentDidChange = 0;
43
44         this._typeTokenScrollHandler = null;
45         this._typeTokenAnnotator = null;
46         this._basicBlockAnnotator = null;
47
48         this._isProbablyMinified = false;
49
50         // FIXME: Currently this just jumps between resources and related source map resources. It doesn't "jump to symbol" yet.
51         this._updateTokenTrackingControllerState();
52
53         this.element.classList.add("source-code");
54
55         if (this._supportsDebugging) {
56             WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.DisabledStateDidChange, this._breakpointStatusDidChange, this);
57             WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.AutoContinueDidChange, this._breakpointStatusDidChange, this);
58             WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.ResolvedStateDidChange, this._breakpointStatusDidChange, this);
59             WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.LocationDidChange, this._updateBreakpointLocation, this);
60
61             WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.BreakpointsEnabledDidChange, this._breakpointsEnabledDidChange, this);
62             WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.BreakpointAdded, this._breakpointAdded, this);
63             WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.BreakpointRemoved, this._breakpointRemoved, this);
64             WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.ActiveCallFrameDidChange, this._activeCallFrameDidChange, this);
65
66             WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Paused, this._debuggerDidPause, this);
67             WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Resumed, this._debuggerDidResume, this);
68             if (WebInspector.debuggerManager.activeCallFrame)
69                 this._debuggerDidPause();
70
71             this._activeCallFrameDidChange();
72         }
73
74         WebInspector.issueManager.addEventListener(WebInspector.IssueManager.Event.IssueWasAdded, this._issueWasAdded, this);
75
76         if (this._sourceCode instanceof WebInspector.SourceMapResource || this._sourceCode.sourceMaps.length > 0)
77             WebInspector.notifications.addEventListener(WebInspector.Notification.GlobalModifierKeysDidChange, this._updateTokenTrackingControllerState, this);
78         else
79             this._sourceCode.addEventListener(WebInspector.SourceCode.Event.SourceMapAdded, this._sourceCodeSourceMapAdded, this);
80
81         sourceCode.requestContent().then(this._contentAvailable.bind(this));
82
83         // FIXME: Cmd+L shorcut doesn't actually work.
84         new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Command, "L", this.showGoToLineDialog.bind(this), this.element);
85         new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Control, "G", this.showGoToLineDialog.bind(this), this.element);
86     }
87
88     // Public
89
90     get sourceCode()
91     {
92         return this._sourceCode;
93     }
94
95     shown()
96     {
97         super.shown();
98
99         if (WebInspector.showJavaScriptTypeInformationSetting.value) {
100             if (this._typeTokenAnnotator)
101                 this._typeTokenAnnotator.resume();
102             if (this._basicBlockAnnotator)
103                 this._basicBlockAnnotator.resume();
104             if (!this._typeTokenScrollHandler && (this._typeTokenAnnotator || this._basicBlockAnnotator))
105                 this._enableScrollEventsForTypeTokenAnnotator();
106         } else {
107             if (this._typeTokenAnnotator || this._basicBlockAnnotator)
108                 this._setTypeTokenAnnotatorEnabledState(false);
109         }
110     }
111
112     hidden()
113     {
114         super.hidden();
115
116         this.tokenTrackingController.removeHighlightedRange();
117
118         this._dismissPopover();
119
120         this._dismissEditingController(true);
121
122         if (this._typeTokenAnnotator)
123             this._typeTokenAnnotator.pause();
124         if (this._basicBlockAnnotator)
125             this._basicBlockAnnotator.pause();
126     }
127
128     close()
129     {
130         if (this._supportsDebugging) {
131             WebInspector.Breakpoint.removeEventListener(null, null, this);
132             WebInspector.debuggerManager.removeEventListener(null, null, this);
133
134             if (this._activeCallFrameSourceCodeLocation) {
135                 this._activeCallFrameSourceCodeLocation.removeEventListener(WebInspector.SourceCodeLocation.Event.LocationChanged, this._activeCallFrameSourceCodeLocationChanged, this);
136                 delete this._activeCallFrameSourceCodeLocation;
137             }
138         }
139
140         WebInspector.issueManager.removeEventListener(WebInspector.IssueManager.Event.IssueWasAdded, this._issueWasAdded, this);
141
142         WebInspector.notifications.removeEventListener(WebInspector.Notification.GlobalModifierKeysDidChange, this._updateTokenTrackingControllerState, this);
143         this._sourceCode.removeEventListener(WebInspector.SourceCode.Event.SourceMapAdded, this._sourceCodeSourceMapAdded, this);
144     }
145
146     canBeFormatted()
147     {
148         // Currently we assume that source map resources are formatted how the author wants it.
149         // We could allow source map resources to be formatted, we would then need to make
150         // SourceCodeLocation watch listen for mappedResource's formatting changes, and keep
151         // a formatted location alongside the regular mapped location.
152         if (this._sourceCode instanceof WebInspector.SourceMapResource)
153             return false;
154
155         return super.canBeFormatted();
156     }
157
158     canShowTypeAnnotations()
159     {
160         return !!this._typeTokenAnnotator;
161     }
162
163     customPerformSearch(query)
164     {
165         function searchResultCallback(error, matches)
166         {
167             // Bail if the query changed since we started.
168             if (this.currentSearchQuery !== query)
169                 return;
170
171             if (error || !matches || !matches.length) {
172                 // Report zero matches.
173                 this.dispatchEventToListeners(WebInspector.TextEditor.Event.NumberOfSearchResultsDidChange);
174                 return;
175             }
176
177             var queryRegex = new RegExp(query.escapeForRegExp(), "gi");
178             var searchResults = [];
179
180             for (var i = 0; i < matches.length; ++i) {
181                 var matchLineNumber = matches[i].lineNumber;
182                 var line = this.line(matchLineNumber);
183                 if (!line)
184                     return;
185
186                 // Reset the last index to reuse the regex on a new line.
187                 queryRegex.lastIndex = 0;
188
189                 // Search the line and mark the ranges.
190                 var lineMatch = null;
191                 while (queryRegex.lastIndex + query.length <= line.length && (lineMatch = queryRegex.exec(line))) {
192                     var resultTextRange = new WebInspector.TextRange(matchLineNumber, lineMatch.index, matchLineNumber, queryRegex.lastIndex);
193                     searchResults.push(resultTextRange);
194                 }
195             }
196
197             this.addSearchResults(searchResults);
198
199             this.dispatchEventToListeners(WebInspector.TextEditor.Event.NumberOfSearchResultsDidChange);
200         }
201
202         if (this.hasEdits())
203             return false;
204
205         if (this._sourceCode instanceof WebInspector.SourceMapResource)
206             return false;
207
208         if (this._sourceCode instanceof WebInspector.Resource)
209             PageAgent.searchInResource(this._sourceCode.parentFrame.id, this._sourceCode.url, query, false, false, searchResultCallback.bind(this));
210         else if (this._sourceCode instanceof WebInspector.Script)
211             DebuggerAgent.searchInContent(this._sourceCode.id, query, false, false, searchResultCallback.bind(this));
212         return true;
213     }
214
215     showGoToLineDialog()
216     {
217         if (!this._goToLineDialog) {
218             this._goToLineDialog = new WebInspector.GoToLineDialog;
219             this._goToLineDialog.delegate = this;
220         }
221
222         this._goToLineDialog.present(this.element);
223     }
224
225     isGoToLineDialogValueValid(goToLineDialog, lineNumber)
226     {
227         return !isNaN(lineNumber) && lineNumber > 0 && lineNumber <= this.lineCount;
228     }
229
230     goToLineDialogValueWasValidated(goToLineDialog, lineNumber)
231     {
232         var position = new WebInspector.SourceCodePosition(lineNumber - 1, 0);
233         var range = new WebInspector.TextRange(lineNumber - 1, 0, lineNumber, 0);
234         this.revealPosition(position, range, false, true);
235     }
236
237     goToLineDialogWasDismissed()
238     {
239         this.focus();
240     }
241
242     contentDidChange(replacedRanges, newRanges)
243     {
244         super.contentDidChange(replacedRanges, newRanges);
245
246         if (this._ignoreContentDidChange > 0)
247             return;
248
249         for (var range of newRanges)
250             this._updateEditableMarkers(range);
251
252         if (this._typeTokenAnnotator || this._basicBlockAnnotator) {
253             this._setTypeTokenAnnotatorEnabledState(false);
254             this._typeTokenAnnotator = null;
255             this._basicBlockAnnotator = null;
256         }
257     }
258
259     toggleTypeAnnotations()
260     {
261         if (!this._typeTokenAnnotator)
262             return false;
263
264         var newActivatedState = !this._typeTokenAnnotator.isActive();
265         if (newActivatedState && this._isProbablyMinified && !this.formatted)
266             this.formatted = true;
267
268         this._setTypeTokenAnnotatorEnabledState(newActivatedState);
269         return newActivatedState;
270     }
271
272     showPopoverForTypes(typeDescription, bounds, title)
273     {
274         var content = document.createElement("div");
275         content.className = "object expandable";
276
277         var titleElement = document.createElement("div");
278         titleElement.className = "title";
279         titleElement.textContent = title;
280         content.appendChild(titleElement);
281
282         var bodyElement = content.appendChild(document.createElement("div"));
283         bodyElement.className = "body";
284
285         var typeTreeView = new WebInspector.TypeTreeView(typeDescription);
286         bodyElement.appendChild(typeTreeView.element);
287
288         this._showPopover(content, bounds);
289     }
290
291     // Protected
292
293     prettyPrint(pretty)
294     {
295         // The annotators must be cleared before pretty printing takes place and resumed
296         // after so that they clear their annotations in a known state and insert new annotations
297         // in the new state.
298         var shouldResumeTypeTokenAnnotator = this._typeTokenAnnotator && this._typeTokenAnnotator.isActive();
299         var shouldResumeBasicBlockAnnotator = this._basicBlockAnnotator && this._basicBlockAnnotator.isActive();
300         if (shouldResumeTypeTokenAnnotator || shouldResumeBasicBlockAnnotator)
301             this._setTypeTokenAnnotatorEnabledState(false);
302
303         super.prettyPrint(pretty);
304
305         if (pretty || !this._isProbablyMinified) {
306             if (shouldResumeTypeTokenAnnotator || shouldResumeBasicBlockAnnotator)
307                 this._setTypeTokenAnnotatorEnabledState(true);
308         } else {
309             console.assert(!pretty && this._isProbablyMinified);
310             if (this._typeTokenAnnotator || this._basicBlockAnnotator)
311                 this._setTypeTokenAnnotatorEnabledState(false);
312         }
313     }
314
315     // Private
316
317     _unformattedLineInfoForEditorLineInfo(lineInfo)
318     {
319         if (this.formatterSourceMap)
320             return this.formatterSourceMap.formattedToOriginal(lineInfo.lineNumber, lineInfo.columnNumber);
321         return lineInfo;
322     }
323
324     _sourceCodeLocationForEditorPosition(position)
325     {
326         var lineInfo = {lineNumber: position.line, columnNumber: position.ch};
327         var unformattedLineInfo = this._unformattedLineInfoForEditorLineInfo(lineInfo);
328         return this.sourceCode.createSourceCodeLocation(unformattedLineInfo.lineNumber, unformattedLineInfo.columnNumber);
329     }
330
331     _editorLineInfoForSourceCodeLocation(sourceCodeLocation)
332     {
333         if (this._sourceCode instanceof WebInspector.SourceMapResource)
334             return {lineNumber: sourceCodeLocation.displayLineNumber, columnNumber: sourceCodeLocation.displayColumnNumber};
335         return {lineNumber: sourceCodeLocation.formattedLineNumber, columnNumber: sourceCodeLocation.formattedColumnNumber};
336     }
337
338     _breakpointForEditorLineInfo(lineInfo)
339     {
340         if (!this._breakpointMap[lineInfo.lineNumber])
341             return null;
342         return this._breakpointMap[lineInfo.lineNumber][lineInfo.columnNumber];
343     }
344
345     _addBreakpointWithEditorLineInfo(breakpoint, lineInfo)
346     {
347         if (!this._breakpointMap[lineInfo.lineNumber])
348             this._breakpointMap[lineInfo.lineNumber] = {};
349
350         this._breakpointMap[lineInfo.lineNumber][lineInfo.columnNumber] = breakpoint;
351     }
352
353     _removeBreakpointWithEditorLineInfo(breakpoint, lineInfo)
354     {
355         console.assert(breakpoint === this._breakpointMap[lineInfo.lineNumber][lineInfo.columnNumber]);
356
357         delete this._breakpointMap[lineInfo.lineNumber][lineInfo.columnNumber];
358
359         if (isEmptyObject(this._breakpointMap[lineInfo.lineNumber]))
360             delete this._breakpointMap[lineInfo.lineNumber];
361     }
362
363     _contentWillPopulate(content)
364     {
365         this.dispatchEventToListeners(WebInspector.SourceCodeTextEditor.Event.ContentWillPopulate);
366
367         // We only do the rest of this work before the first populate.
368         if (this._contentPopulated)
369             return;
370
371         if (this._supportsDebugging) {
372             this._breakpointMap = {};
373
374             var breakpoints = WebInspector.debuggerManager.breakpointsForSourceCode(this._sourceCode);
375             for (var i = 0; i < breakpoints.length; ++i) {
376                 var breakpoint = breakpoints[i];
377                 console.assert(this._matchesBreakpoint(breakpoint));
378                 var lineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
379                 this._addBreakpointWithEditorLineInfo(breakpoint, lineInfo);
380                 this.setBreakpointInfoForLineAndColumn(lineInfo.lineNumber, lineInfo.columnNumber, this._breakpointInfoForBreakpoint(breakpoint));
381             }
382         }
383
384         if (this._sourceCode instanceof WebInspector.Resource)
385             this.mimeType = this._sourceCode.syntheticMIMEType;
386         else if (this._sourceCode instanceof WebInspector.Script)
387             this.mimeType = "text/javascript";
388
389         // Automatically format the content if it looks minified and it can be formatted.
390         console.assert(!this.formatted);
391         if (this.canBeFormatted()) {
392             var lastNewlineIndex = 0;
393             while (true) {
394                 var nextNewlineIndex = content.indexOf("\n", lastNewlineIndex);
395                 if (nextNewlineIndex === -1) {
396                     if (content.length - lastNewlineIndex > WebInspector.SourceCodeTextEditor.AutoFormatMinimumLineLength) {
397                         this.autoFormat = true;
398                         this._isProbablyMinified = true;
399                     }
400                     break;
401                 }
402
403                 if (nextNewlineIndex - lastNewlineIndex > WebInspector.SourceCodeTextEditor.AutoFormatMinimumLineLength) {
404                     this.autoFormat = true;
405                     this._isProbablyMinified = true;
406                     break;
407                 }
408
409                 lastNewlineIndex = nextNewlineIndex + 1;
410             }
411         }
412     }
413
414     _contentDidPopulate()
415     {
416         this._contentPopulated = true;
417
418         this.dispatchEventToListeners(WebInspector.SourceCodeTextEditor.Event.ContentDidPopulate);
419
420         // We add the issues each time content is populated. This is needed because lines might not exist
421         // if we tried added them before when the full content wasn't available. (When populating with
422         // partial script content this can be called multiple times.)
423
424         this._reinsertAllIssues();
425
426         this._updateEditableMarkers();
427     }
428
429     _populateWithContent(content)
430     {
431         content = content || "";
432
433         this._contentWillPopulate(content);
434         this.string = content;
435
436         this._makeTypeTokenAnnotator();
437         this._makeBasicBlockAnnotator();
438
439         if (WebInspector.showJavaScriptTypeInformationSetting.value) {
440             if (this._basicBlockAnnotator || this._typeTokenAnnotator)
441                 this._setTypeTokenAnnotatorEnabledState(true);
442         }
443
444         this._contentDidPopulate();
445     }
446
447     _contentAvailable(parameters)
448     {
449         // Return if resource is not available.
450         if (parameters.error)
451             return;
452
453         var sourceCode = parameters.sourceCode;
454         var content = sourceCode.content;
455         var base64Encoded = parameters.base64Encoded;
456
457         console.assert(sourceCode === this._sourceCode);
458         console.assert(!base64Encoded);
459
460         // Abort if the full content populated while waiting for this async callback.
461         if (this._fullContentPopulated)
462             return;
463
464         this._fullContentPopulated = true;
465         this._invalidLineNumbers = {};
466
467         this._populateWithContent(content);
468     }
469
470     _breakpointStatusDidChange(event)
471     {
472         this._updateBreakpointStatus(event.target);
473     }
474
475     _breakpointsEnabledDidChange()
476     {
477         console.assert(this._supportsDebugging);
478
479         var breakpoints = WebInspector.debuggerManager.breakpointsForSourceCode(this._sourceCode);
480         for (var breakpoint of breakpoints)
481             this._updateBreakpointStatus(breakpoint);
482     }
483
484     _updateBreakpointStatus(breakpoint)
485     {
486         console.assert(this._supportsDebugging);
487
488         if (!this._contentPopulated)
489             return;
490
491         if (!this._matchesBreakpoint(breakpoint))
492             return;
493
494         var lineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
495         this.setBreakpointInfoForLineAndColumn(lineInfo.lineNumber, lineInfo.columnNumber, this._breakpointInfoForBreakpoint(breakpoint));
496     }
497
498     _updateBreakpointLocation(event)
499     {
500         console.assert(this._supportsDebugging);
501
502         if (!this._contentPopulated)
503             return;
504
505         var breakpoint = event.target;
506         if (!this._matchesBreakpoint(breakpoint))
507             return;
508
509         if (this._ignoreAllBreakpointLocationUpdates)
510             return;
511
512         if (breakpoint === this._ignoreLocationUpdateBreakpoint)
513             return;
514
515         var sourceCodeLocation = breakpoint.sourceCodeLocation;
516
517         if (this._sourceCode instanceof WebInspector.SourceMapResource) {
518             // Update our breakpoint location if the display location changed.
519             if (sourceCodeLocation.displaySourceCode !== this._sourceCode)
520                 return;
521             var oldLineInfo = {lineNumber: event.data.oldDisplayLineNumber, columnNumber: event.data.oldDisplayColumnNumber};
522             var newLineInfo = {lineNumber: sourceCodeLocation.displayLineNumber, columnNumber: sourceCodeLocation.displayColumnNumber};
523         } else {
524             // Update our breakpoint location if the original location changed.
525             if (sourceCodeLocation.sourceCode !== this._sourceCode)
526                 return;
527             var oldLineInfo = {lineNumber: event.data.oldFormattedLineNumber, columnNumber: event.data.oldFormattedColumnNumber};
528             var newLineInfo = {lineNumber: sourceCodeLocation.formattedLineNumber, columnNumber: sourceCodeLocation.formattedColumnNumber};
529         }
530
531         var existingBreakpoint = this._breakpointForEditorLineInfo(oldLineInfo);
532         if (!existingBreakpoint)
533             return;
534
535         console.assert(breakpoint === existingBreakpoint);
536
537         this.setBreakpointInfoForLineAndColumn(oldLineInfo.lineNumber, oldLineInfo.columnNumber, null);
538         this.setBreakpointInfoForLineAndColumn(newLineInfo.lineNumber, newLineInfo.columnNumber, this._breakpointInfoForBreakpoint(breakpoint));
539
540         this._removeBreakpointWithEditorLineInfo(breakpoint, oldLineInfo);
541         this._addBreakpointWithEditorLineInfo(breakpoint, newLineInfo);
542     }
543
544     _breakpointAdded(event)
545     {
546         console.assert(this._supportsDebugging);
547
548         if (!this._contentPopulated)
549             return;
550
551         var breakpoint = event.data.breakpoint;
552         if (!this._matchesBreakpoint(breakpoint))
553             return;
554
555         if (breakpoint === this._ignoreBreakpointAddedBreakpoint)
556             return;
557
558         var lineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
559         this._addBreakpointWithEditorLineInfo(breakpoint, lineInfo);
560         this.setBreakpointInfoForLineAndColumn(lineInfo.lineNumber, lineInfo.columnNumber, this._breakpointInfoForBreakpoint(breakpoint));
561     }
562
563     _breakpointRemoved(event)
564     {
565         console.assert(this._supportsDebugging);
566
567         if (!this._contentPopulated)
568             return;
569
570         var breakpoint = event.data.breakpoint;
571         if (!this._matchesBreakpoint(breakpoint))
572             return;
573
574         if (breakpoint === this._ignoreBreakpointRemovedBreakpoint)
575             return;
576
577         var lineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
578         this._removeBreakpointWithEditorLineInfo(breakpoint, lineInfo);
579         this.setBreakpointInfoForLineAndColumn(lineInfo.lineNumber, lineInfo.columnNumber, null);
580     }
581
582     _activeCallFrameDidChange()
583     {
584         console.assert(this._supportsDebugging);
585
586         if (this._activeCallFrameSourceCodeLocation) {
587             this._activeCallFrameSourceCodeLocation.removeEventListener(WebInspector.SourceCodeLocation.Event.LocationChanged, this._activeCallFrameSourceCodeLocationChanged, this);
588             delete this._activeCallFrameSourceCodeLocation;
589         }
590
591         var activeCallFrame = WebInspector.debuggerManager.activeCallFrame;
592         if (!activeCallFrame || !this._matchesSourceCodeLocation(activeCallFrame.sourceCodeLocation)) {
593             this.executionLineNumber = NaN;
594             this.executionColumnNumber = NaN;
595             return;
596         }
597
598         this._dismissPopover();
599
600         this._activeCallFrameSourceCodeLocation = activeCallFrame.sourceCodeLocation;
601         this._activeCallFrameSourceCodeLocation.addEventListener(WebInspector.SourceCodeLocation.Event.LocationChanged, this._activeCallFrameSourceCodeLocationChanged, this);
602
603         // Don't return early if the line number didn't change. The execution state still
604         // could have changed (e.g. continuing in a loop with a breakpoint inside).
605
606         var lineInfo = this._editorLineInfoForSourceCodeLocation(activeCallFrame.sourceCodeLocation);
607         this.executionLineNumber = lineInfo.lineNumber;
608         this.executionColumnNumber = lineInfo.columnNumber;
609
610         // If we have full content or this source code isn't a Resource we can return early.
611         // Script source code populates from the request started in the constructor.
612         if (this._fullContentPopulated || !(this._sourceCode instanceof WebInspector.Resource) || this._requestingScriptContent)
613             return;
614
615         // Since we are paused in the debugger we need to show some content, and since the Resource
616         // content hasn't populated yet we need to populate with content from the Scripts by URL.
617         // Document resources will attempt to populate the scripts as inline (in <script> tags.)
618         // Other resources are assumed to be full scripts (JavaScript resources).
619         if (this._sourceCode.type === WebInspector.Resource.Type.Document)
620             this._populateWithInlineScriptContent();
621         else
622             this._populateWithScriptContent();
623     }
624
625     _activeCallFrameSourceCodeLocationChanged(event)
626     {
627         console.assert(!isNaN(this.executionLineNumber));
628         if (isNaN(this.executionLineNumber))
629             return;
630
631         console.assert(WebInspector.debuggerManager.activeCallFrame);
632         console.assert(this._activeCallFrameSourceCodeLocation === WebInspector.debuggerManager.activeCallFrame.sourceCodeLocation);
633
634         var lineInfo = this._editorLineInfoForSourceCodeLocation(this._activeCallFrameSourceCodeLocation);
635         this.executionLineNumber = lineInfo.lineNumber;
636         this.executionColumnNumber = lineInfo.columnNumber;
637     }
638
639     _populateWithInlineScriptContent()
640     {
641         console.assert(this._sourceCode instanceof WebInspector.Resource);
642         console.assert(!this._fullContentPopulated);
643         console.assert(!this._requestingScriptContent);
644
645         var scripts = this._sourceCode.scripts;
646         console.assert(scripts.length);
647         if (!scripts.length)
648             return;
649
650         var pendingRequestCount = scripts.length;
651
652         // If the number of scripts hasn't change since the last populate, then there is nothing to do.
653         if (this._inlineScriptContentPopulated === pendingRequestCount)
654             return;
655
656         this._inlineScriptContentPopulated = pendingRequestCount;
657
658         function scriptContentAvailable(parameters)
659         {
660             // Return early if we are still waiting for content from other scripts.
661             if (--pendingRequestCount)
662                 return;
663
664             delete this._requestingScriptContent;
665
666             // Abort if the full content populated while waiting for these async callbacks.
667             if (this._fullContentPopulated)
668                 return;
669
670             var scriptOpenTag = "<script>";
671             var scriptCloseTag = "</script>";
672
673             var content = "";
674             var lineNumber = 0;
675             var columnNumber = 0;
676
677             this._invalidLineNumbers = {};
678
679             for (var i = 0; i < scripts.length; ++i) {
680                 // Fill the line gap with newline characters.
681                 for (var newLinesCount = scripts[i].range.startLine - lineNumber; newLinesCount > 0; --newLinesCount) {
682                     if (!columnNumber)
683                         this._invalidLineNumbers[scripts[i].range.startLine - newLinesCount] = true;
684                     columnNumber = 0;
685                     content += "\n";
686                 }
687
688                 // Fill the column gap with space characters.
689                 for (var spacesCount = scripts[i].range.startColumn - columnNumber - scriptOpenTag.length; spacesCount > 0; --spacesCount)
690                     content += " ";
691
692                 // Add script tags and content.
693                 content += scriptOpenTag;
694                 content += scripts[i].content;
695                 content += scriptCloseTag;
696
697                 lineNumber = scripts[i].range.endLine;
698                 columnNumber = scripts[i].range.endColumn + scriptCloseTag.length;
699             }
700
701             this._populateWithContent(content);
702         }
703
704         this._requestingScriptContent = true;
705
706         var boundScriptContentAvailable = scriptContentAvailable.bind(this);
707         for (var i = 0; i < scripts.length; ++i)
708             scripts[i].requestContent().then(boundScriptContentAvailable);
709     }
710
711     _populateWithScriptContent()
712     {
713         console.assert(this._sourceCode instanceof WebInspector.Resource);
714         console.assert(!this._fullContentPopulated);
715         console.assert(!this._requestingScriptContent);
716
717         // We can assume this resource only has one script that starts at line/column 0.
718         var scripts = this._sourceCode.scripts;
719         console.assert(scripts.length === 1);
720         if (!scripts.length)
721             return;
722
723         console.assert(scripts[0].range.startLine === 0);
724         console.assert(scripts[0].range.startColumn === 0);
725
726         function scriptContentAvailable(parameters)
727         {
728             var content = parameters.content;
729             delete this._requestingScriptContent;
730
731             // Abort if the full content populated while waiting for this async callback.
732             if (this._fullContentPopulated)
733                 return;
734
735             // This is the full content.
736             this._fullContentPopulated = true;
737
738             this._populateWithContent(content);
739         }
740
741         this._requestingScriptContent = true;
742
743         scripts[0].requestContent().then(scriptContentAvailable.bind(this));
744     }
745
746     _matchesSourceCodeLocation(sourceCodeLocation)
747     {
748         if (this._sourceCode instanceof WebInspector.SourceMapResource)
749             return sourceCodeLocation.displaySourceCode === this._sourceCode;
750         if (this._sourceCode instanceof WebInspector.Resource)
751             return sourceCodeLocation.sourceCode.url === this._sourceCode.url;
752         if (this._sourceCode instanceof WebInspector.Script)
753             return sourceCodeLocation.sourceCode === this._sourceCode;
754         return false;
755     }
756
757     _matchesBreakpoint(breakpoint)
758     {
759         console.assert(this._supportsDebugging);
760         if (this._sourceCode instanceof WebInspector.SourceMapResource)
761             return breakpoint.sourceCodeLocation.displaySourceCode === this._sourceCode;
762         if (this._sourceCode instanceof WebInspector.Resource)
763             return breakpoint.url === this._sourceCode.url;
764         if (this._sourceCode instanceof WebInspector.Script)
765             return breakpoint.url === this._sourceCode.url || breakpoint.scriptIdentifier === this._sourceCode.id;
766         return false;
767     }
768
769     _matchesIssue(issue)
770     {
771         if (this._sourceCode instanceof WebInspector.Resource)
772             return issue.url === this._sourceCode.url;
773         // FIXME: Support issues for Scripts based on id, not only by URL.
774         if (this._sourceCode instanceof WebInspector.Script)
775             return issue.url === this._sourceCode.url;
776         return false;
777     }
778
779     _issueWasAdded(event)
780     {
781         var issue = event.data.issue;
782         if (!this._matchesIssue(issue))
783             return;
784
785         this._addIssue(issue);
786     }
787
788     _addIssue(issue)
789     {
790         // FIXME: Issue should have a SourceCodeLocation.
791         var sourceCodeLocation = this._sourceCode.createSourceCodeLocation(issue.lineNumber, issue.columnNumber);
792         var lineNumber = sourceCodeLocation.formattedLineNumber;
793
794         var lineNumberIssues = this._issuesLineNumberMap.get(lineNumber);
795         if (!lineNumberIssues) {
796             lineNumberIssues = [];
797             this._issuesLineNumberMap.set(lineNumber, lineNumberIssues);
798         }
799
800         // Avoid displaying duplicate issues on the same line.
801         for (var existingIssue of lineNumberIssues) {
802             if (existingIssue.columnNumber === issue.columnNumber && existingIssue.text === issue.text)
803                 return;
804         }
805
806         lineNumberIssues.push(issue);
807
808         if (issue.level === WebInspector.IssueMessage.Level.Error)
809             this.addStyleClassToLine(lineNumber, WebInspector.SourceCodeTextEditor.LineErrorStyleClassName);
810         else if (issue.level === WebInspector.IssueMessage.Level.Warning)
811             this.addStyleClassToLine(lineNumber, WebInspector.SourceCodeTextEditor.LineWarningStyleClassName);
812         else
813             console.error("Unknown issue level");
814
815         var widget = this._issueWidgetForLine(lineNumber);
816         if (widget) {
817             if (issue.level === WebInspector.IssueMessage.Level.Error)
818                 widget.widgetElement.classList.add(WebInspector.SourceCodeTextEditor.LineErrorStyleClassName);
819             else if (issue.level === WebInspector.IssueMessage.Level.Warning)
820                 widget.widgetElement.classList.add(WebInspector.SourceCodeTextEditor.LineWarningStyleClassName);
821
822             this._updateIssueWidgetForIssues(widget, lineNumberIssues);
823         }
824     }
825
826     _issueWidgetForLine(lineNumber)
827     {
828         var widget = this._widgetMap.get(lineNumber);
829         if (widget)
830             return widget;
831
832         widget = this.createWidgetForLine(lineNumber);
833         if (!widget)
834             return null;
835
836         var widgetElement = widget.widgetElement;
837         widgetElement.classList.add("issue-widget", "inline");
838         widgetElement.addEventListener("click", this._handleWidgetClick.bind(this, widget, lineNumber));
839
840         this._widgetMap.set(lineNumber, widget);
841
842         return widget;
843     }
844
845     _iconClassNameForIssueLevel(level)
846     {
847         if (level === WebInspector.IssueMessage.Level.Warning)
848             return "icon-warning";
849
850         console.assert(level === WebInspector.IssueMessage.Level.Error);
851         return "icon-error";
852     }
853
854     _updateIssueWidgetForIssues(widget, issues)
855     {
856         var widgetElement = widget.widgetElement;
857         widgetElement.removeChildren();
858
859         if (widgetElement.classList.contains("inline") || issues.length === 1) {
860             var arrowElement = widgetElement.appendChild(document.createElement("span"));
861             arrowElement.className = "arrow";
862
863             var iconElement = widgetElement.appendChild(document.createElement("span"));
864             iconElement.className = "icon";
865
866             var textElement = widgetElement.appendChild(document.createElement("span"));
867             textElement.className = "text";
868
869             if (issues.length === 1) {
870                 iconElement.classList.add(this._iconClassNameForIssueLevel(issues[0].level));
871                 textElement.textContent = issues[0].text;
872             } else {
873                 var errorsCount = 0;
874                 var warningsCount = 0;
875                 for (var issue of issues) {
876                     if (issue.level === WebInspector.IssueMessage.Level.Error)
877                         ++errorsCount;
878                     else if (issue.level === WebInspector.IssueMessage.Level.Warning)
879                         ++warningsCount;
880                 }
881
882                 if (warningsCount && errorsCount) {
883                     iconElement.classList.add(this._iconClassNameForIssueLevel(issue.level));
884                     textElement.textContent = WebInspector.UIString("%d Errors, %d Warnings").format(errorsCount, warningsCount);
885                 } else if (errorsCount) {
886                     iconElement.classList.add(this._iconClassNameForIssueLevel(issue.level));
887                     textElement.textContent = WebInspector.UIString("%d Errors").format(errorsCount);
888                 } else if (warningsCount) {
889                     iconElement.classList.add(this._iconClassNameForIssueLevel(issue.level));
890                     textElement.textContent = WebInspector.UIString("%d Warnings").format(warningsCount);
891                 }
892
893                 widget[WebInspector.SourceCodeTextEditor.WidgetContainsMultipleIssuesSymbol] = true;
894             }
895         } else {
896             for (var issue of issues) {
897                 var iconElement = widgetElement.appendChild(document.createElement("span"));
898                 iconElement.className = "icon";
899                 iconElement.classList.add(this._iconClassNameForIssueLevel(issue.level));
900
901                 var textElement = widgetElement.appendChild(document.createElement("span"));
902                 textElement.className = "text";
903                 textElement.textContent = issue.text;
904
905                 widgetElement.appendChild(document.createElement("br"));
906             }
907         }
908
909         widget.update();
910     }
911
912     _isWidgetToggleable(widget)
913     {
914         if (widget[WebInspector.SourceCodeTextEditor.WidgetContainsMultipleIssuesSymbol])
915             return true;
916
917         if (!widget.widgetElement.classList.contains("inline"))
918             return true;
919
920         var textElement = widget.widgetElement.lastChild;
921         if (textElement.offsetWidth !== textElement.scrollWidth)
922             return true;
923
924         return false;
925     }
926
927     _handleWidgetClick(widget, lineNumber, event)
928     {
929         if (!this._isWidgetToggleable(widget))
930             return;
931
932         widget.widgetElement.classList.toggle("inline");
933
934         var lineNumberIssues = this._issuesLineNumberMap.get(lineNumber);
935         this._updateIssueWidgetForIssues(widget, lineNumberIssues);
936     }
937
938     _breakpointInfoForBreakpoint(breakpoint)
939     {
940         return {resolved: breakpoint.resolved, disabled: breakpoint.disabled, autoContinue: breakpoint.autoContinue};
941     }
942
943     get _supportsDebugging()
944     {
945         if (this._sourceCode instanceof WebInspector.Resource)
946             return this._sourceCode.type === WebInspector.Resource.Type.Document || this._sourceCode.type === WebInspector.Resource.Type.Script;
947         if (this._sourceCode instanceof WebInspector.Script)
948             return true;
949         return false;
950     }
951
952     // TextEditor Delegate
953
954     textEditorBaseURL(textEditor)
955     {
956         return this._sourceCode.url;
957     }
958
959     textEditorShouldHideLineNumber(textEditor, lineNumber)
960     {
961         return lineNumber in this._invalidLineNumbers;
962     }
963
964     textEditorGutterContextMenu(textEditor, lineNumber, columnNumber, editorBreakpoints, event)
965     {
966         if (!this._supportsDebugging)
967             return;
968
969         event.preventDefault();
970
971         function continueToLocation()
972         {
973             WebInspector.debuggerManager.continueToLocation(script.id, sourceCodeLocation.lineNumber, sourceCodeLocation.columnNumber);
974         }
975
976         function addBreakpoint()
977         {
978             var data = this.textEditorBreakpointAdded(this, lineNumber, columnNumber);
979             this.setBreakpointInfoForLineAndColumn(data.lineNumber, data.columnNumber, data.breakpointInfo);
980         }
981
982         function revealInSidebar()
983         {
984             WebInspector.showDebuggerTab(breakpoint);
985         }
986
987         var contextMenu = new WebInspector.ContextMenu(event);
988
989         // Paused. Add Continue to Here option only if we have a script identifier for the location.
990         if (WebInspector.debuggerManager.paused) {
991             var editorLineInfo = {lineNumber, columnNumber};
992             var unformattedLineInfo = this._unformattedLineInfoForEditorLineInfo(editorLineInfo);
993             var sourceCodeLocation = this._sourceCode.createSourceCodeLocation(unformattedLineInfo.lineNumber, unformattedLineInfo.columnNumber);
994
995             if (sourceCodeLocation.sourceCode instanceof WebInspector.Script)
996                 var script = sourceCodeLocation.sourceCode;
997             else if (sourceCodeLocation.sourceCode instanceof WebInspector.Resource)
998                 var script = sourceCodeLocation.sourceCode.scriptForLocation(sourceCodeLocation);
999
1000             if (script) {
1001
1002                 contextMenu.appendItem(WebInspector.UIString("Continue to Here"), continueToLocation);
1003                 contextMenu.appendSeparator();
1004             }
1005         }
1006
1007         var breakpoints = [];
1008         for (var i = 0; i < editorBreakpoints.length; ++i) {
1009             var lineInfo = editorBreakpoints[i];
1010             var breakpoint = this._breakpointForEditorLineInfo(lineInfo);
1011             console.assert(breakpoint);
1012             if (breakpoint)
1013                 breakpoints.push(breakpoint);
1014         }
1015
1016         // No breakpoints.
1017         if (!breakpoints.length) {
1018
1019             contextMenu.appendItem(WebInspector.UIString("Add Breakpoint"), addBreakpoint.bind(this));
1020             contextMenu.show();
1021             return;
1022         }
1023
1024         // Single breakpoint.
1025         if (breakpoints.length === 1) {
1026             WebInspector.breakpointPopoverController.appendContextMenuItems(contextMenu, breakpoints[0], event.target);
1027
1028             if (!WebInspector.isShowingDebuggerTab()) {
1029                 contextMenu.appendSeparator();
1030                 contextMenu.appendItem(WebInspector.UIString("Reveal in Debugger Tab"), revealInSidebar);
1031             }
1032
1033             contextMenu.show();
1034             return;
1035         }
1036
1037         // Multiple breakpoints.
1038         var shouldDisable = false;
1039         for (var i = 0; i < breakpoints.length; ++i) {
1040             if (!breakpoints[i].disabled) {
1041                 shouldDisable = true;
1042                 break;
1043             }
1044         }
1045
1046         function removeBreakpoints()
1047         {
1048             for (var i = 0; i < breakpoints.length; ++i) {
1049                 var breakpoint = breakpoints[i];
1050                 if (WebInspector.debuggerManager.isBreakpointRemovable(breakpoint))
1051                     WebInspector.debuggerManager.removeBreakpoint(breakpoint);
1052             }
1053         }
1054
1055         function toggleBreakpoints()
1056         {
1057             for (var i = 0; i < breakpoints.length; ++i)
1058                 breakpoints[i].disabled = shouldDisable;
1059         }
1060
1061         if (shouldDisable)
1062             contextMenu.appendItem(WebInspector.UIString("Disable Breakpoints"), toggleBreakpoints.bind(this));
1063         else
1064             contextMenu.appendItem(WebInspector.UIString("Enable Breakpoints"), toggleBreakpoints.bind(this));
1065         contextMenu.appendItem(WebInspector.UIString("Delete Breakpoints"), removeBreakpoints.bind(this));
1066         contextMenu.show();
1067     }
1068
1069     textEditorBreakpointAdded(textEditor, lineNumber, columnNumber)
1070     {
1071         if (!this._supportsDebugging)
1072             return null;
1073
1074         var editorLineInfo = {lineNumber, columnNumber};
1075         var unformattedLineInfo = this._unformattedLineInfoForEditorLineInfo(editorLineInfo);
1076         var sourceCodeLocation = this._sourceCode.createSourceCodeLocation(unformattedLineInfo.lineNumber, unformattedLineInfo.columnNumber);
1077         var breakpoint = new WebInspector.Breakpoint(sourceCodeLocation);
1078
1079         var lineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
1080         this._addBreakpointWithEditorLineInfo(breakpoint, lineInfo);
1081
1082         this._ignoreBreakpointAddedBreakpoint = breakpoint;
1083
1084         var shouldSkipEventDispatch = false;
1085         var shouldSpeculativelyResolveBreakpoint = true;
1086         WebInspector.debuggerManager.addBreakpoint(breakpoint, shouldSkipEventDispatch, shouldSpeculativelyResolveBreakpoint);
1087         delete this._ignoreBreakpointAddedBreakpoint;
1088
1089         // Return the more accurate location and breakpoint info.
1090         return {
1091             breakpointInfo: this._breakpointInfoForBreakpoint(breakpoint),
1092             lineNumber: lineInfo.lineNumber,
1093             columnNumber: lineInfo.columnNumber
1094         };
1095     }
1096
1097     textEditorBreakpointRemoved(textEditor, lineNumber, columnNumber)
1098     {
1099         console.assert(this._supportsDebugging);
1100         if (!this._supportsDebugging)
1101             return;
1102
1103         var lineInfo = {lineNumber, columnNumber};
1104         var breakpoint = this._breakpointForEditorLineInfo(lineInfo);
1105         console.assert(breakpoint);
1106         if (!breakpoint)
1107             return;
1108
1109         this._removeBreakpointWithEditorLineInfo(breakpoint, lineInfo);
1110
1111         this._ignoreBreakpointRemovedBreakpoint = breakpoint;
1112         WebInspector.debuggerManager.removeBreakpoint(breakpoint);
1113         delete this._ignoreBreakpointAddedBreakpoint;
1114     }
1115
1116     textEditorBreakpointMoved(textEditor, oldLineNumber, oldColumnNumber, newLineNumber, newColumnNumber)
1117     {
1118         console.assert(this._supportsDebugging);
1119         if (!this._supportsDebugging)
1120             return;
1121
1122         var oldLineInfo = {lineNumber: oldLineNumber, columnNumber: oldColumnNumber};
1123         var breakpoint = this._breakpointForEditorLineInfo(oldLineInfo);
1124         console.assert(breakpoint);
1125         if (!breakpoint)
1126             return;
1127
1128         this._removeBreakpointWithEditorLineInfo(breakpoint, oldLineInfo);
1129
1130         var newLineInfo = {lineNumber: newLineNumber, columnNumber: newColumnNumber};
1131         var unformattedNewLineInfo = this._unformattedLineInfoForEditorLineInfo(newLineInfo);
1132         this._ignoreLocationUpdateBreakpoint = breakpoint;
1133         breakpoint.sourceCodeLocation.update(this._sourceCode, unformattedNewLineInfo.lineNumber, unformattedNewLineInfo.columnNumber);
1134         delete this._ignoreLocationUpdateBreakpoint;
1135
1136         var accurateNewLineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
1137         this._addBreakpointWithEditorLineInfo(breakpoint, accurateNewLineInfo);
1138
1139         if (accurateNewLineInfo.lineNumber !== newLineInfo.lineNumber || accurateNewLineInfo.columnNumber !== newLineInfo.columnNumber)
1140             this.updateBreakpointLineAndColumn(newLineInfo.lineNumber, newLineInfo.columnNumber, accurateNewLineInfo.lineNumber, accurateNewLineInfo.columnNumber);
1141     }
1142
1143     textEditorBreakpointClicked(textEditor, lineNumber, columnNumber)
1144     {
1145         console.assert(this._supportsDebugging);
1146         if (!this._supportsDebugging)
1147             return;
1148
1149         var breakpoint = this._breakpointForEditorLineInfo({lineNumber, columnNumber});
1150         console.assert(breakpoint);
1151         if (!breakpoint)
1152             return;
1153
1154         breakpoint.cycleToNextMode();
1155     }
1156
1157     textEditorUpdatedFormatting(textEditor)
1158     {
1159         this._ignoreAllBreakpointLocationUpdates = true;
1160         this._sourceCode.formatterSourceMap = this.formatterSourceMap;
1161         delete this._ignoreAllBreakpointLocationUpdates;
1162
1163         // Always put the source map on both the Script and Resource if both exist. For example,
1164         // if this SourceCode is a Resource, then there might also be a Script. In the debugger,
1165         // the backend identifies call frames with Script line and column information, and the
1166         // Script needs the formatter source map to produce the proper display line and column.
1167         if (this._sourceCode instanceof WebInspector.Resource && !(this._sourceCode instanceof WebInspector.SourceMapResource)) {
1168             var scripts = this._sourceCode.scripts;
1169             for (var i = 0; i < scripts.length; ++i)
1170                 scripts[i].formatterSourceMap = this.formatterSourceMap;
1171         } else if (this._sourceCode instanceof WebInspector.Script) {
1172             if (this._sourceCode.resource)
1173                 this._sourceCode.resource.formatterSourceMap = this.formatterSourceMap;
1174         }
1175
1176         // Some breakpoints / issues may have moved, some might not have. Just go through
1177         // and remove and reinsert all the breakpoints / issues.
1178
1179         var oldBreakpointMap = this._breakpointMap;
1180         this._breakpointMap = {};
1181
1182         for (var lineNumber in oldBreakpointMap) {
1183             for (var columnNumber in oldBreakpointMap[lineNumber]) {
1184                 var breakpoint = oldBreakpointMap[lineNumber][columnNumber];
1185                 var newLineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
1186                 this._addBreakpointWithEditorLineInfo(breakpoint, newLineInfo);
1187                 this.setBreakpointInfoForLineAndColumn(lineNumber, columnNumber, null);
1188                 this.setBreakpointInfoForLineAndColumn(newLineInfo.lineNumber, newLineInfo.columnNumber, this._breakpointInfoForBreakpoint(breakpoint));
1189             }
1190         }
1191
1192         this._reinsertAllIssues();
1193     }
1194
1195     _clearWidgets()
1196     {
1197         for (var widget of this._widgetMap.values())
1198             widget.clear();
1199
1200         this._widgetMap.clear();
1201     }
1202
1203     _reinsertAllIssues()
1204     {
1205         this._issuesLineNumberMap.clear();
1206         this._clearWidgets();
1207
1208         var issues = WebInspector.issueManager.issuesForSourceCode(this._sourceCode);
1209         for (var issue of issues) {
1210             console.assert(this._matchesIssue(issue));
1211             this._addIssue(issue);
1212         }
1213     }
1214
1215     _debuggerDidPause(event)
1216     {
1217         this._updateTokenTrackingControllerState();
1218         if (this._typeTokenAnnotator && this._typeTokenAnnotator.isActive())
1219             this._typeTokenAnnotator.refresh();
1220         if (this._basicBlockAnnotator && this._basicBlockAnnotator.isActive())
1221             this._basicBlockAnnotator.refresh();
1222     }
1223
1224     _debuggerDidResume(event)
1225     {
1226         this._updateTokenTrackingControllerState();
1227         this._dismissPopover();
1228         if (this._typeTokenAnnotator && this._typeTokenAnnotator.isActive())
1229             this._typeTokenAnnotator.refresh();
1230         if (this._basicBlockAnnotator && this._basicBlockAnnotator.isActive())
1231             this._basicBlockAnnotator.refresh();
1232     }
1233
1234     _sourceCodeSourceMapAdded(event)
1235     {
1236         WebInspector.notifications.addEventListener(WebInspector.Notification.GlobalModifierKeysDidChange, this._updateTokenTrackingControllerState, this);
1237         this._sourceCode.removeEventListener(WebInspector.SourceCode.Event.SourceMapAdded, this._sourceCodeSourceMapAdded, this);
1238
1239         this._updateTokenTrackingControllerState();
1240     }
1241
1242     _updateTokenTrackingControllerState()
1243     {
1244         var mode = WebInspector.CodeMirrorTokenTrackingController.Mode.None;
1245         if (WebInspector.debuggerManager.paused)
1246             mode = WebInspector.CodeMirrorTokenTrackingController.Mode.JavaScriptExpression;
1247         else if (this._typeTokenAnnotator && this._typeTokenAnnotator.isActive())
1248             mode = WebInspector.CodeMirrorTokenTrackingController.Mode.JavaScriptTypeInformation;
1249         else if (this._hasColorMarkers())
1250             mode = WebInspector.CodeMirrorTokenTrackingController.Mode.MarkedTokens;
1251         else if ((this._sourceCode instanceof WebInspector.SourceMapResource || this._sourceCode.sourceMaps.length !== 0) && WebInspector.modifierKeys.metaKey && !WebInspector.modifierKeys.altKey && !WebInspector.modifierKeys.shiftKey)
1252             mode = WebInspector.CodeMirrorTokenTrackingController.Mode.NonSymbolTokens;
1253
1254         this.tokenTrackingController.enabled = mode !== WebInspector.CodeMirrorTokenTrackingController.Mode.None;
1255
1256         if (mode === this.tokenTrackingController.mode)
1257             return;
1258
1259         switch (mode) {
1260         case WebInspector.CodeMirrorTokenTrackingController.Mode.MarkedTokens:
1261             this.tokenTrackingController.mouseOverDelayDuration = 0;
1262             this.tokenTrackingController.mouseOutReleaseDelayDuration = 0;
1263             break;
1264         case WebInspector.CodeMirrorTokenTrackingController.Mode.NonSymbolTokens:
1265             this.tokenTrackingController.mouseOverDelayDuration = 0;
1266             this.tokenTrackingController.mouseOutReleaseDelayDuration = 0;
1267             this.tokenTrackingController.classNameForHighlightedRange = WebInspector.CodeMirrorTokenTrackingController.JumpToSymbolHighlightStyleClassName;
1268             this._dismissPopover();
1269             break;
1270         case WebInspector.CodeMirrorTokenTrackingController.Mode.JavaScriptExpression:
1271         case WebInspector.CodeMirrorTokenTrackingController.Mode.JavaScriptTypeInformation:
1272             this.tokenTrackingController.mouseOverDelayDuration = WebInspector.SourceCodeTextEditor.DurationToMouseOverTokenToMakeHoveredToken;
1273             this.tokenTrackingController.mouseOutReleaseDelayDuration = WebInspector.SourceCodeTextEditor.DurationToMouseOutOfHoveredTokenToRelease;
1274             this.tokenTrackingController.classNameForHighlightedRange = WebInspector.SourceCodeTextEditor.HoveredExpressionHighlightStyleClassName;
1275             break;
1276         }
1277
1278         this.tokenTrackingController.mode = mode;
1279     }
1280
1281     _hasColorMarkers()
1282     {
1283         for (var marker of this.markers) {
1284             if (marker.type === WebInspector.TextMarker.Type.Color)
1285                 return true;
1286         }
1287         return false;
1288     }
1289
1290     // CodeMirrorTokenTrackingController Delegate
1291
1292     tokenTrackingControllerCanReleaseHighlightedRange(tokenTrackingController, element)
1293     {
1294         if (!this._popover)
1295             return true;
1296
1297         if (!window.getSelection().isCollapsed && this._popover.element.contains(window.getSelection().anchorNode))
1298             return false;
1299
1300         return true;
1301     }
1302
1303     tokenTrackingControllerHighlightedRangeReleased(tokenTrackingController, forceHide = false)
1304     {
1305         if (forceHide || !this._mouseIsOverPopover)
1306             this._dismissPopover();
1307     }
1308
1309     tokenTrackingControllerHighlightedRangeWasClicked(tokenTrackingController)
1310     {
1311         if (this.tokenTrackingController.mode !== WebInspector.CodeMirrorTokenTrackingController.Mode.NonSymbolTokens)
1312             return;
1313
1314         // Links are handled by TextEditor.
1315         if (/\blink\b/.test(this.tokenTrackingController.candidate.hoveredToken.type))
1316             return;
1317
1318         var sourceCodeLocation = this._sourceCodeLocationForEditorPosition(this.tokenTrackingController.candidate.hoveredTokenRange.start);
1319         if (this.sourceCode instanceof WebInspector.SourceMapResource)
1320             WebInspector.showOriginalOrFormattedSourceCodeLocation(sourceCodeLocation);
1321         else
1322             WebInspector.showSourceCodeLocation(sourceCodeLocation);
1323     }
1324
1325     tokenTrackingControllerNewHighlightCandidate(tokenTrackingController, candidate)
1326     {
1327         if (this.tokenTrackingController.mode === WebInspector.CodeMirrorTokenTrackingController.Mode.NonSymbolTokens) {
1328             this.tokenTrackingController.highlightRange(candidate.hoveredTokenRange);
1329             return;
1330         }
1331
1332         if (this.tokenTrackingController.mode === WebInspector.CodeMirrorTokenTrackingController.Mode.JavaScriptExpression) {
1333             this._tokenTrackingControllerHighlightedJavaScriptExpression(candidate);
1334             return;
1335         }
1336
1337         if (this.tokenTrackingController.mode === WebInspector.CodeMirrorTokenTrackingController.Mode.JavaScriptTypeInformation) {
1338             this._tokenTrackingControllerHighlightedJavaScriptTypeInformation(candidate);
1339             return;
1340         }
1341
1342         if (this.tokenTrackingController.mode === WebInspector.CodeMirrorTokenTrackingController.Mode.MarkedTokens) {
1343             var markers = this.markersAtPosition(candidate.hoveredTokenRange.start);
1344             if (markers.length > 0)
1345                 this._tokenTrackingControllerHighlightedMarkedExpression(candidate, markers);
1346             else
1347                 this._dismissEditingController();
1348         }
1349     }
1350
1351     tokenTrackingControllerMouseOutOfHoveredMarker(tokenTrackingController, hoveredMarker)
1352     {
1353         this._dismissEditingController();
1354     }
1355
1356     _tokenTrackingControllerHighlightedJavaScriptExpression(candidate)
1357     {
1358         console.assert(candidate.expression);
1359
1360         function populate(error, result, wasThrown)
1361         {
1362             if (error || wasThrown)
1363                 return;
1364
1365             if (candidate !== this.tokenTrackingController.candidate)
1366                 return;
1367
1368             var data = WebInspector.RemoteObject.fromPayload(result);
1369             switch (data.type) {
1370             case "function":
1371                 this._showPopoverForFunction(data);
1372                 break;
1373             case "object":
1374                 if (data.subtype === "null" || data.subtype === "regexp")
1375                     this._showPopoverWithFormattedValue(data);
1376                 else
1377                     this._showPopoverForObject(data);
1378                 break;
1379             case "string":
1380             case "number":
1381             case "boolean":
1382             case "undefined":
1383             case "symbol":
1384                 this._showPopoverWithFormattedValue(data);
1385                 break;
1386             }
1387         }
1388
1389         var expression = appendWebInspectorSourceURL(candidate.expression);
1390
1391         if (WebInspector.debuggerManager.activeCallFrame) {
1392             DebuggerAgent.evaluateOnCallFrame.invoke({callFrameId: WebInspector.debuggerManager.activeCallFrame.id, expression, objectGroup: "popover", doNotPauseOnExceptionsAndMuteConsole: true}, populate.bind(this));
1393             return;
1394         }
1395
1396         // No call frame available. Use the main page's context.
1397         RuntimeAgent.evaluate.invoke({expression, objectGroup: "popover", doNotPauseOnExceptionsAndMuteConsole: true}, populate.bind(this));
1398     }
1399
1400     _tokenTrackingControllerHighlightedJavaScriptTypeInformation(candidate)
1401     {
1402         console.assert(candidate.expression);
1403
1404         var sourceCode = this._sourceCode;
1405         var sourceID = sourceCode instanceof WebInspector.Script ? sourceCode.id : sourceCode.scripts[0].id;
1406         var range = candidate.hoveredTokenRange;
1407         var offset = this.currentPositionToOriginalOffset({line: range.start.line, ch: range.start.ch});
1408
1409         var allRequests = [{
1410             typeInformationDescriptor: WebInspector.ScriptSyntaxTree.TypeProfilerSearchDescriptor.NormalExpression,
1411             sourceID,
1412             divot: offset
1413         }];
1414
1415         function handler(error, allTypes) {
1416             if (error)
1417                 return;
1418
1419             if (candidate !== this.tokenTrackingController.candidate)
1420                 return;
1421
1422             console.assert(allTypes.length === 1);
1423             if (!allTypes.length)
1424                 return;
1425
1426             var typeDescription = WebInspector.TypeDescription.fromPayload(allTypes[0]);
1427             if (typeDescription.valid) {
1428                 var popoverTitle = WebInspector.TypeTokenView.titleForPopover(WebInspector.TypeTokenView.TitleType.Variable, candidate.expression);
1429                 this.showPopoverForTypes(typeDescription, null, popoverTitle);
1430             }
1431         }
1432
1433         RuntimeAgent.getRuntimeTypesForVariablesAtOffsets(allRequests, handler.bind(this));
1434     }
1435
1436     _showPopover(content, bounds)
1437     {
1438         console.assert(this.tokenTrackingController.candidate || bounds);
1439
1440         var shouldHighlightRange = false;
1441         var candidate = this.tokenTrackingController.candidate;
1442         // If bounds is falsey, this is a popover introduced from a hover event.
1443         // Otherwise, this is called from TypeTokenAnnotator.
1444         if (!bounds) {
1445             if (!candidate)
1446                 return;
1447
1448             var rects = this.rectsForRange(candidate.hoveredTokenRange);
1449             bounds = WebInspector.Rect.unionOfRects(rects);
1450
1451             shouldHighlightRange = true;
1452         }
1453
1454         content.classList.add(WebInspector.SourceCodeTextEditor.PopoverDebuggerContentStyleClassName);
1455
1456         this._popover = this._popover || new WebInspector.Popover(this);
1457         this._popover.presentNewContentWithFrame(content, bounds.pad(5), [WebInspector.RectEdge.MIN_Y, WebInspector.RectEdge.MAX_Y, WebInspector.RectEdge.MAX_X]);
1458         if (shouldHighlightRange)
1459             this.tokenTrackingController.highlightRange(candidate.expressionRange);
1460
1461         this._trackPopoverEvents();
1462     }
1463
1464     _showPopoverForFunction(data)
1465     {
1466         var candidate = this.tokenTrackingController.candidate;
1467
1468         function didGetDetails(error, response)
1469         {
1470             if (error) {
1471                 console.error(error);
1472                 this._dismissPopover();
1473                 return;
1474             }
1475
1476             // Nothing to do if the token has changed since the time we
1477             // asked for the function details from the backend.
1478             if (candidate !== this.tokenTrackingController.candidate)
1479                 return;
1480
1481             var wrapper = document.createElement("div");
1482             wrapper.className = "body formatted-function";
1483             wrapper.textContent = data.description;
1484
1485             var content = document.createElement("div");
1486             content.className = "function";
1487
1488             var title = content.appendChild(document.createElement("div"));
1489             title.className = "title";
1490             title.textContent = response.name || response.inferredName || response.displayName || WebInspector.UIString("(anonymous function)");
1491
1492             content.appendChild(wrapper);
1493
1494             this._showPopover(content);
1495         }
1496         DebuggerAgent.getFunctionDetails(data.objectId, didGetDetails.bind(this));
1497     }
1498
1499     _showPopoverForObject(data)
1500     {
1501         var content = document.createElement("div");
1502         content.className = "object expandable";
1503
1504         var titleElement = document.createElement("div");
1505         titleElement.className = "title";
1506         titleElement.textContent = data.description;
1507         content.appendChild(titleElement);
1508
1509         if (data.subtype === "node") {
1510             data.pushNodeToFrontend(function(nodeId) {
1511                 if (!nodeId)
1512                     return;
1513
1514                 var domNode = WebInspector.domTreeManager.nodeForId(nodeId);
1515                 if (!domNode.ownerDocument)
1516                     return;
1517
1518                 var goToButton = titleElement.appendChild(WebInspector.createGoToArrowButton());
1519                 goToButton.addEventListener("click", function() {
1520                     WebInspector.domTreeManager.inspectElement(nodeId);
1521                 });
1522             });
1523         }
1524
1525         // FIXME: If this is a variable, it would be nice to put the variable name in the PropertyPath.
1526         var objectTree = new WebInspector.ObjectTreeView(data);
1527         objectTree.showOnlyProperties();
1528         objectTree.expand();
1529
1530         var bodyElement = content.appendChild(document.createElement("div"));
1531         bodyElement.className = "body";
1532         bodyElement.appendChild(objectTree.element);
1533
1534         // Show the popover once we have the first set of properties for the object.
1535         var candidate = this.tokenTrackingController.candidate;
1536         objectTree.addEventListener(WebInspector.ObjectTreeView.Event.Updated, function() {
1537             if (candidate === this.tokenTrackingController.candidate)
1538                 this._showPopover(content);
1539             objectTree.removeEventListener(null, null, this);
1540         }, this);
1541     }
1542
1543     _showPopoverWithFormattedValue(remoteObject)
1544     {
1545         var content = WebInspector.FormattedValue.createElementForRemoteObject(remoteObject);
1546         this._showPopover(content);
1547     }
1548
1549     willDismissPopover(popover)
1550     {
1551         this.tokenTrackingController.removeHighlightedRange();
1552
1553         RuntimeAgent.releaseObjectGroup("popover");
1554     }
1555
1556     _dismissPopover()
1557     {
1558         if (!this._popover)
1559             return;
1560
1561         this._popover.dismiss();
1562
1563         if (this._popoverEventListeners && this._popoverEventListenersAreRegistered) {
1564             this._popoverEventListenersAreRegistered = false;
1565             this._popoverEventListeners.unregister();
1566         }
1567     }
1568
1569     _trackPopoverEvents()
1570     {
1571         if (!this._popoverEventListeners)
1572             this._popoverEventListeners = new WebInspector.EventListenerSet(this, "Popover listeners");
1573         if (!this._popoverEventListenersAreRegistered) {
1574             this._popoverEventListenersAreRegistered = true;
1575             this._popoverEventListeners.register(this._popover.element, "mouseover", this._popoverMouseover);
1576             this._popoverEventListeners.register(this._popover.element, "mouseout", this._popoverMouseout);
1577             this._popoverEventListeners.install();
1578         }
1579     }
1580
1581     _popoverMouseover(event)
1582     {
1583         this._mouseIsOverPopover = true;
1584     }
1585
1586     _popoverMouseout(event)
1587     {
1588         this._mouseIsOverPopover = this._popover.element.contains(event.relatedTarget);
1589     }
1590
1591     _updateEditableMarkers(range)
1592     {
1593         this.createColorMarkers(range);
1594         this.createGradientMarkers(range);
1595         this.createCubicBezierMarkers(range);
1596
1597         this._updateTokenTrackingControllerState();
1598     }
1599
1600     _tokenTrackingControllerHighlightedMarkedExpression(candidate, markers)
1601     {
1602         // Look for the outermost editable marker.
1603         var editableMarker;
1604         for (var marker of markers) {
1605             if (!marker.range || (marker.type !== WebInspector.TextMarker.Type.Color && marker.type !== WebInspector.TextMarker.Type.Gradient && marker.type !== WebInspector.TextMarker.Type.CubicBezier))
1606                 continue;
1607
1608             if (!editableMarker || (marker.range.startLine < editableMarker.range.startLine || (marker.range.startLine === editableMarker.range.startLine && marker.range.startColumn < editableMarker.range.startColumn)))
1609                 editableMarker = marker;
1610         }
1611
1612         if (!editableMarker) {
1613             this.tokenTrackingController.hoveredMarker = null;
1614             return;
1615         }
1616
1617         if (this.tokenTrackingController.hoveredMarker === editableMarker)
1618             return;
1619
1620         this._dismissEditingController();
1621
1622         this.tokenTrackingController.hoveredMarker = editableMarker;
1623
1624         this._editingController = this.editingControllerForMarker(editableMarker);
1625
1626         if (marker.type === WebInspector.TextMarker.Type.Color) {
1627             var color = this._editingController.value;
1628             if (!color || !color.valid) {
1629                 editableMarker.clear();
1630                 delete this._editingController;
1631                 return;
1632             }
1633         }
1634
1635         this._editingController.delegate = this;
1636         this._editingController.presentHoverMenu();
1637     }
1638
1639     _dismissEditingController(discrete)
1640     {
1641         if (this._editingController)
1642             this._editingController.dismissHoverMenu(discrete);
1643
1644         this.tokenTrackingController.hoveredMarker = null;
1645         delete this._editingController;
1646     }
1647
1648     // CodeMirrorEditingController Delegate
1649
1650     editingControllerDidStartEditing(editingController)
1651     {
1652         // We can pause the token tracking controller during editing, it will be reset
1653         // to the expected state by calling _updateEditableMarkers() in the
1654         // editingControllerDidFinishEditing delegate.
1655         this.tokenTrackingController.enabled = false;
1656
1657         // We clear the marker since we'll reset it after editing.
1658         editingController.marker.clear();
1659
1660         // We ignore content changes made as a result of color editing.
1661         this._ignoreContentDidChange++;
1662     }
1663
1664     editingControllerDidFinishEditing(editingController)
1665     {
1666         this._updateEditableMarkers(editingController.range);
1667
1668         this._ignoreContentDidChange--;
1669
1670         delete this._editingController;
1671     }
1672
1673     _setTypeTokenAnnotatorEnabledState(shouldActivate)
1674     {
1675         console.assert(this._typeTokenAnnotator);
1676         if (!this._typeTokenAnnotator)
1677             return;
1678
1679         if (shouldActivate) {
1680             console.assert(this.visible, "Annotators should not be enabled if the TextEditor is not visible");
1681
1682             RuntimeAgent.enableTypeProfiler();
1683
1684             this._typeTokenAnnotator.reset();
1685             if (this._basicBlockAnnotator) {
1686                 console.assert(!this._basicBlockAnnotator.isActive());
1687                 this._basicBlockAnnotator.reset();
1688             }
1689
1690             if (!this._typeTokenScrollHandler)
1691                 this._enableScrollEventsForTypeTokenAnnotator();
1692         } else {
1693             // Because we disable type profiling when exiting the inspector, there is no need to call
1694             // RuntimeAgent.disableTypeProfiler() here.  If we were to call it here, JavaScriptCore would
1695             // compile out all the necessary type profiling information, so if a user were to quickly press then
1696             // unpress the type profiling button, we wouldn't be able to re-show type information which would
1697             // provide a confusing user experience.
1698
1699             this._typeTokenAnnotator.clear();
1700             if (this._basicBlockAnnotator)
1701                 this._basicBlockAnnotator.clear();
1702
1703             if (this._typeTokenScrollHandler)
1704                 this._disableScrollEventsForTypeTokenAnnotator();
1705         }
1706
1707         WebInspector.showJavaScriptTypeInformationSetting.value = shouldActivate;
1708
1709         this._updateTokenTrackingControllerState();
1710     }
1711
1712     _getAssociatedScript()
1713     {
1714         var script = null;
1715         // FIXME: This needs to me modified to work with HTML files with inline script tags.
1716         if (this._sourceCode instanceof WebInspector.Script)
1717             script = this._sourceCode;
1718         else if (this._sourceCode instanceof WebInspector.Resource && this._sourceCode.type === WebInspector.Resource.Type.Script && this._sourceCode.scripts.length)
1719             script = this._sourceCode.scripts[0];
1720         return script;
1721     }
1722
1723     _makeTypeTokenAnnotator()
1724     {
1725         // COMPATIBILITY (iOS 8): Runtime.getRuntimeTypesForVariablesAtOffsets did not exist yet.
1726         if (!RuntimeAgent.getRuntimeTypesForVariablesAtOffsets)
1727             return;
1728
1729         var script = this._getAssociatedScript();
1730         if (!script)
1731             return;
1732
1733         this._typeTokenAnnotator = new WebInspector.TypeTokenAnnotator(this, script);
1734     }
1735
1736     _makeBasicBlockAnnotator()
1737     {
1738         // COMPATIBILITY (iOS 8): Runtime.getBasicBlocks did not exist yet.
1739         if (!RuntimeAgent.getBasicBlocks)
1740             return;
1741
1742         var script = this._getAssociatedScript();
1743         if (!script)
1744             return;
1745
1746         this._basicBlockAnnotator = new WebInspector.BasicBlockAnnotator(this, script);
1747     }
1748
1749     _enableScrollEventsForTypeTokenAnnotator()
1750     {
1751         // Pause updating type tokens while scrolling to prevent frame loss.
1752         console.assert(!this._typeTokenScrollHandler);
1753         this._typeTokenScrollHandler = this._makeTypeTokenScrollEventHandler();
1754         this.addScrollHandler(this._typeTokenScrollHandler);
1755     }
1756
1757     _disableScrollEventsForTypeTokenAnnotator()
1758     {
1759         console.assert(this._typeTokenScrollHandler);
1760         this.removeScrollHandler(this._typeTokenScrollHandler);
1761         this._typeTokenScrollHandler = null;
1762     }
1763
1764     _makeTypeTokenScrollEventHandler()
1765     {
1766         var timeoutIdentifier = null;
1767         function scrollHandler()
1768         {
1769             if (timeoutIdentifier)
1770                 clearTimeout(timeoutIdentifier);
1771             else {
1772                 if (this._typeTokenAnnotator)
1773                     this._typeTokenAnnotator.pause();
1774                 if (this._basicBlockAnnotator)
1775                     this._basicBlockAnnotator.pause();
1776             }
1777
1778             timeoutIdentifier = setTimeout(function() {
1779                 timeoutIdentifier = null;
1780                 if (this._typeTokenAnnotator)
1781                     this._typeTokenAnnotator.resume();
1782                 if (this._basicBlockAnnotator)
1783                     this._basicBlockAnnotator.resume();
1784             }.bind(this), WebInspector.SourceCodeTextEditor.DurationToUpdateTypeTokensAfterScrolling);
1785         }
1786
1787         return scrollHandler.bind(this);
1788     }
1789 };
1790
1791 WebInspector.SourceCodeTextEditor.LineErrorStyleClassName = "error";
1792 WebInspector.SourceCodeTextEditor.LineWarningStyleClassName = "warning";
1793 WebInspector.SourceCodeTextEditor.PopoverDebuggerContentStyleClassName = "debugger-popover-content";
1794 WebInspector.SourceCodeTextEditor.HoveredExpressionHighlightStyleClassName = "hovered-expression-highlight";
1795 WebInspector.SourceCodeTextEditor.DurationToMouseOverTokenToMakeHoveredToken = 500;
1796 WebInspector.SourceCodeTextEditor.DurationToMouseOutOfHoveredTokenToRelease = 1000;
1797 WebInspector.SourceCodeTextEditor.DurationToUpdateTypeTokensAfterScrolling = 100;
1798 WebInspector.SourceCodeTextEditor.AutoFormatMinimumLineLength = 500;
1799 WebInspector.SourceCodeTextEditor.WidgetContainsMultipleIssuesSymbol = Symbol("source-code-widget-contains-multiple-issues");
1800
1801 WebInspector.SourceCodeTextEditor.Event = {
1802     ContentWillPopulate: "source-code-text-editor-content-will-populate",
1803     ContentDidPopulate: "source-code-text-editor-content-did-populate"
1804 };