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