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