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