Web Inspector: DOM: provide a way to disable/breakpoint all event listeners for a...
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / SourceCodeTextEditor.js
index 0920e96..2f16baa 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-WebInspector.SourceCodeTextEditor = function(sourceCode)
+WI.SourceCodeTextEditor = class SourceCodeTextEditor extends WI.TextEditor
 {
-    console.assert(sourceCode instanceof WebInspector.SourceCode);
-
-    this._sourceCode = sourceCode;
-    this._breakpointMap = {};
-    this._issuesLineNumberMap = {};
-    this._contentPopulated = false;
-    this._invalidLineNumbers = {0: true};
-    this._ignoreContentDidChange = 0;
-
-    WebInspector.TextEditor.call(this, null, null, this);
-
-    this._typeTokenScrollHandler = null;
-    this._typeTokenAnnotator = null;
-    this._basicBlockAnnotator = null;
-    
-    this._isProbablyMinified = false;
+    constructor(sourceCode)
+    {
+        console.assert(sourceCode instanceof WI.SourceCode);
 
-    // FIXME: Currently this just jumps between resources and related source map resources. It doesn't "jump to symbol" yet.
-    this._updateTokenTrackingControllerState();
+        super();
 
-    this.element.classList.add(WebInspector.SourceCodeTextEditor.StyleClassName);
+        this.delegate = this;
 
-    if (this._supportsDebugging) {
-        WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.DisabledStateDidChange, this._breakpointStatusDidChange, this);
-        WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.AutoContinueDidChange, this._breakpointStatusDidChange, this);
-        WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.ResolvedStateDidChange, this._breakpointStatusDidChange, this);
-        WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.LocationDidChange, this._updateBreakpointLocation, this);
+        this._sourceCode = sourceCode;
+        this._breakpointMap = {};
+        this._issuesLineNumberMap = new Map;
+        this._widgetMap = new Map;
+        this._contentPopulated = false;
+        this._invalidLineNumbers = {0: true};
+        this._requestingScriptContent = false;
+        this._activeCallFrameSourceCodeLocation = null;
 
-        WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.BreakpointsEnabledDidChange, this._breakpointsEnabledDidChange, this);
-        WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.BreakpointAdded, this._breakpointAdded, this);
-        WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.BreakpointRemoved, this._breakpointRemoved, this);
-        WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.ActiveCallFrameDidChange, this._activeCallFrameDidChange, this);
+        this._threadLineNumberMap = new Map; // line -> [targets]
+        this._threadWidgetMap = new Map; // line -> widget
+        this._threadTargetMap = new Map; // target -> line
 
-        WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Paused, this._debuggerDidPause, this);
-        WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Resumed, this._debuggerDidResume, this);
-        if (WebInspector.debuggerManager.activeCallFrame)
-            this._debuggerDidPause();
+        this._typeTokenScrollHandler = null;
+        this._typeTokenAnnotator = null;
+        this._basicBlockAnnotator = null;
+        this._editingController = null;
 
-        this._activeCallFrameDidChange();
-    }
+        this._autoFormat = false;
+        this._isProbablyMinified = false;
 
-    WebInspector.issueManager.addEventListener(WebInspector.IssueManager.Event.IssueWasAdded, this._issueWasAdded, this);
+        this._ignoreContentDidChange = 0;
+        this._ignoreLocationUpdateBreakpoint = null;
+        this._ignoreBreakpointAddedBreakpoint = null;
+        this._ignoreBreakpointRemovedBreakpoint = null;
+        this._ignoreAllBreakpointLocationUpdates = false;
 
-    if (this._sourceCode instanceof WebInspector.SourceMapResource || this._sourceCode.sourceMaps.length > 0)
-        WebInspector.notifications.addEventListener(WebInspector.Notification.GlobalModifierKeysDidChange, this._updateTokenTrackingControllerState, this);
-    else
-        this._sourceCode.addEventListener(WebInspector.SourceCode.Event.SourceMapAdded, this._sourceCodeSourceMapAdded, this);
+        // FIXME: Currently this just jumps between resources and related source map resources. It doesn't "jump to symbol" yet.
+        this._updateTokenTrackingControllerState();
 
-    sourceCode.requestContent().then(this._contentAvailable.bind(this));
+        this.element.classList.add("source-code");
 
-    // FIXME: Cmd+L shorcut doesn't actually work.
-    new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Command, "L", this.showGoToLineDialog.bind(this), this.element);
-    new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Control, "G", this.showGoToLineDialog.bind(this), this.element);
-};
+        if (this._supportsDebugging) {
+            WI.Breakpoint.addEventListener(WI.Breakpoint.Event.DisabledStateDidChange, this._breakpointStatusDidChange, this);
+            WI.Breakpoint.addEventListener(WI.Breakpoint.Event.AutoContinueDidChange, this._breakpointStatusDidChange, this);
+            WI.Breakpoint.addEventListener(WI.Breakpoint.Event.ResolvedStateDidChange, this._breakpointStatusDidChange, this);
+            WI.Breakpoint.addEventListener(WI.Breakpoint.Event.LocationDidChange, this._updateBreakpointLocation, this);
+
+            WI.targetManager.addEventListener(WI.TargetManager.Event.TargetAdded, this._targetAdded, this);
+            WI.targetManager.addEventListener(WI.TargetManager.Event.TargetRemoved, this._targetRemoved, this);
+
+            WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.BreakpointsEnabledDidChange, this._breakpointsEnabledDidChange, this);
+            WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.BreakpointAdded, this._breakpointAdded, this);
+            WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.BreakpointRemoved, this._breakpointRemoved, this);
+            WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.CallFramesDidChange, this._callFramesDidChange, this);
+            WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ActiveCallFrameDidChange, this._activeCallFrameDidChange, this);
+
+            WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.Paused, this._debuggerDidPause, this);
+            WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.Resumed, this._debuggerDidResume, this);
+            if (WI.debuggerManager.activeCallFrame)
+                this._debuggerDidPause();
+
+            this._activeCallFrameDidChange();
+        }
 
-WebInspector.Object.addConstructorFunctions(WebInspector.SourceCodeTextEditor);
+        WI.consoleManager.addEventListener(WI.ConsoleManager.Event.IssueAdded, this._issueWasAdded, this);
 
-WebInspector.SourceCodeTextEditor.StyleClassName = "source-code";
-WebInspector.SourceCodeTextEditor.LineErrorStyleClassName = "error";
-WebInspector.SourceCodeTextEditor.LineWarningStyleClassName = "warning";
-WebInspector.SourceCodeTextEditor.PopoverDebuggerContentStyleClassName = "debugger-popover-content";
-WebInspector.SourceCodeTextEditor.HoveredExpressionHighlightStyleClassName = "hovered-expression-highlight";
-WebInspector.SourceCodeTextEditor.DurationToMouseOverTokenToMakeHoveredToken = 500;
-WebInspector.SourceCodeTextEditor.DurationToMouseOutOfHoveredTokenToRelease = 1000;
-WebInspector.SourceCodeTextEditor.DurationToUpdateTypeTokensAfterScrolling = 100;
+        this._sourceCode.addEventListener(WI.SourceCode.Event.FormatterDidChange, this._handleFormatterDidChange, this);
+        if (this._sourceCode instanceof WI.SourceMapResource || this._sourceCode.sourceMaps.length > 0)
+            WI.notifications.addEventListener(WI.Notification.GlobalModifierKeysDidChange, this._updateTokenTrackingControllerState, this);
+        else
+            this._sourceCode.addEventListener(WI.SourceCode.Event.SourceMapAdded, this._sourceCodeSourceMapAdded, this);
 
-WebInspector.SourceCodeTextEditor.AutoFormatMinimumLineLength = 500;
+        sourceCode.requestContent().then(this._contentAvailable.bind(this));
 
-WebInspector.SourceCodeTextEditor.Event = {
-    ContentWillPopulate: "source-code-text-editor-content-will-populate",
-    ContentDidPopulate: "source-code-text-editor-content-did-populate"
-};
+        new WI.KeyboardShortcut(WI.KeyboardShortcut.Modifier.Control, "G", this.showGoToLineDialog.bind(this), this.element);
 
-WebInspector.SourceCodeTextEditor.prototype = {
-    constructor: WebInspector.SourceCodeTextEditor,
+        WI.consoleManager.addEventListener(WI.ConsoleManager.Event.Cleared, this._logCleared, this);
+    }
 
     // Public
 
     get sourceCode()
     {
         return this._sourceCode;
-    },
+    }
+
+    get target()
+    {
+        if (this._sourceCode instanceof WI.SourceMapResource) {
+            if (this._sourceCode.sourceMap.originalSourceCode instanceof WI.Script)
+                return this._sourceCode.sourceMap.originalSourceCode.target;
+        }
+
+        if (this._sourceCode instanceof WI.Script)
+            return this._sourceCode.target;
+
+        return WI.mainTarget;
+    }
 
-    shown: function()
+    shown()
     {
-        WebInspector.TextEditor.prototype.shown.call(this);
+        super.shown();
 
-        if (WebInspector.showJavaScriptTypeInformationSetting.value) {
+        if (WI.settings.showJavaScriptTypeInformation.value) {
             if (this._typeTokenAnnotator)
                 this._typeTokenAnnotator.resume();
-            if (this._basicBlockAnnotator)
-                this._basicBlockAnnotator.resume();
-            if (!this._typeTokenScrollHandler && (this._typeTokenAnnotator || this._basicBlockAnnotator))
+            if (!this._typeTokenScrollHandler && this._typeTokenAnnotator)
                 this._enableScrollEventsForTypeTokenAnnotator();
         } else {
-            if (this._typeTokenAnnotator || this._basicBlockAnnotator)
+            if (this._typeTokenAnnotator)
                 this._setTypeTokenAnnotatorEnabledState(false);
         }
-    },
 
-    hidden: function()
+        if (WI.settings.enableControlFlowProfiler.value) {
+            if (this._basicBlockAnnotator)
+                this._basicBlockAnnotator.resume();
+
+            if (!this._controlFlowScrollHandler && this._basicBlockAnnotator)
+                this._enableScrollEventsForControlFlowAnnotator();
+        } else {
+            this._basicBlockAnnotatorEnabled = false;
+        }
+    }
+
+    hidden()
     {
-        WebInspector.TextEditor.prototype.hidden.call(this);
+        super.hidden();
 
         this.tokenTrackingController.removeHighlightedRange();
 
         this._dismissPopover();
-        
+
         this._dismissEditingController(true);
 
         if (this._typeTokenAnnotator)
             this._typeTokenAnnotator.pause();
         if (this._basicBlockAnnotator)
             this._basicBlockAnnotator.pause();
-    },
+    }
 
-    close: function()
+    close()
     {
         if (this._supportsDebugging) {
-            WebInspector.Breakpoint.removeEventListener(WebInspector.Breakpoint.Event.DisabledStateDidChange, this._breakpointStatusDidChange, this);
-            WebInspector.Breakpoint.removeEventListener(WebInspector.Breakpoint.Event.AutoContinueDidChange, this._breakpointStatusDidChange, this);
-            WebInspector.Breakpoint.removeEventListener(WebInspector.Breakpoint.Event.ResolvedStateDidChange, this._breakpointStatusDidChange, this);
-            WebInspector.Breakpoint.removeEventListener(WebInspector.Breakpoint.Event.LocationDidChange, this._updateBreakpointLocation, this);
-
-            WebInspector.debuggerManager.removeEventListener(WebInspector.DebuggerManager.Event.BreakpointsEnabledDidChange, this._breakpointsEnabledDidChange, this);
-            WebInspector.debuggerManager.removeEventListener(WebInspector.DebuggerManager.Event.BreakpointAdded, this._breakpointAdded, this);
-            WebInspector.debuggerManager.removeEventListener(WebInspector.DebuggerManager.Event.BreakpointRemoved, this._breakpointRemoved, this);
-            WebInspector.debuggerManager.removeEventListener(WebInspector.DebuggerManager.Event.ActiveCallFrameDidChange, this._activeCallFrameDidChange, this);
+            WI.Breakpoint.removeEventListener(null, null, this);
+            WI.debuggerManager.removeEventListener(null, null, this);
+            WI.targetManager.removeEventListener(null, null, this);
 
             if (this._activeCallFrameSourceCodeLocation) {
-                this._activeCallFrameSourceCodeLocation.removeEventListener(WebInspector.SourceCodeLocation.Event.LocationChanged, this._activeCallFrameSourceCodeLocationChanged, this);
-                delete this._activeCallFrameSourceCodeLocation;
+                this._activeCallFrameSourceCodeLocation.removeEventListener(WI.SourceCodeLocation.Event.LocationChanged, this._activeCallFrameSourceCodeLocationChanged, this);
+                this._activeCallFrameSourceCodeLocation = null;
             }
         }
 
-        WebInspector.issueManager.removeEventListener(WebInspector.IssueManager.Event.IssueWasAdded, this._issueWasAdded, this);
-
-        WebInspector.notifications.removeEventListener(WebInspector.Notification.GlobalModifierKeysDidChange, this._updateTokenTrackingControllerState, this);
-        this._sourceCode.removeEventListener(WebInspector.SourceCode.Event.SourceMapAdded, this._sourceCodeSourceMapAdded, this);
-    },
+        WI.consoleManager.removeEventListener(null, null, this);
+        WI.notifications.removeEventListener(null, null, this);
+        this._sourceCode.removeEventListener(null, null, this);
+    }
 
-    canBeFormatted: function()
+    canBeFormatted()
     {
         // Currently we assume that source map resources are formatted how the author wants it.
         // We could allow source map resources to be formatted, we would then need to make
         // SourceCodeLocation watch listen for mappedResource's formatting changes, and keep
         // a formatted location alongside the regular mapped location.
-        if (this._sourceCode instanceof WebInspector.SourceMapResource)
+        if (this._sourceCode instanceof WI.SourceMapResource)
             return false;
 
-        return WebInspector.TextEditor.prototype.canBeFormatted.call(this);
-    },
+        return super.canBeFormatted();
+    }
 
-    canShowTypeAnnotations: function()
+    canShowTypeAnnotations()
     {
-        return !!this._typeTokenAnnotator;
-    },
+        // Type annotations for modified scripts are currently unsupported.
+        return !!this._getAssociatedScript() && !this.hasModified;
+    }
 
-    customPerformSearch: function(query)
+    canShowCoverageHints()
+    {
+        // Code coverage hints for modified scripts are currently unsupported.
+        return !!this._getAssociatedScript() && !this.hasModified;
+    }
+
+    customPerformSearch(query)
     {
         function searchResultCallback(error, matches)
         {
@@ -193,16 +216,18 @@ WebInspector.SourceCodeTextEditor.prototype = {
 
             if (error || !matches || !matches.length) {
                 // Report zero matches.
-                this.dispatchEventToListeners(WebInspector.TextEditor.Event.NumberOfSearchResultsDidChange);
+                this.dispatchEventToListeners(WI.TextEditor.Event.NumberOfSearchResultsDidChange);
                 return;
             }
 
-            var queryRegex = new RegExp(query.escapeForRegExp(), "gi");
+            let queryRegex = WI.SearchUtilities.regExpForString(query, WI.SearchUtilities.defaultSettings);
             var searchResults = [];
 
             for (var i = 0; i < matches.length; ++i) {
                 var matchLineNumber = matches[i].lineNumber;
                 var line = this.line(matchLineNumber);
+                if (!line)
+                    return;
 
                 // Reset the last index to reuse the regex on a new line.
                 queryRegex.lastIndex = 0;
@@ -210,59 +235,55 @@ WebInspector.SourceCodeTextEditor.prototype = {
                 // Search the line and mark the ranges.
                 var lineMatch = null;
                 while (queryRegex.lastIndex + query.length <= line.length && (lineMatch = queryRegex.exec(line))) {
-                    var resultTextRange = new WebInspector.TextRange(matchLineNumber, lineMatch.index, matchLineNumber, queryRegex.lastIndex);
+                    var resultTextRange = new WI.TextRange(matchLineNumber, lineMatch.index, matchLineNumber, queryRegex.lastIndex);
                     searchResults.push(resultTextRange);
                 }
             }
 
             this.addSearchResults(searchResults);
 
-            this.dispatchEventToListeners(WebInspector.TextEditor.Event.NumberOfSearchResultsDidChange);
+            this.dispatchEventToListeners(WI.TextEditor.Event.NumberOfSearchResultsDidChange);
         }
 
         if (this.hasEdits())
             return false;
 
-        if (this._sourceCode instanceof WebInspector.SourceMapResource)
+        if (this._sourceCode instanceof WI.SourceMapResource)
             return false;
 
-        if (this._sourceCode instanceof WebInspector.Resource)
-            PageAgent.searchInResource(this._sourceCode.parentFrame.id, this._sourceCode.url, query, false, false, searchResultCallback.bind(this));
-        else if (this._sourceCode instanceof WebInspector.Script)
-            DebuggerAgent.searchInContent(this._sourceCode.id, query, false, false, searchResultCallback.bind(this));
+        let caseSensitive = WI.SearchUtilities.defaultSettings.caseSensitive.value;
+        let isRegex = WI.SearchUtilities.defaultSettings.regularExpression.value;
+
+        if (this._sourceCode instanceof WI.Resource)
+            PageAgent.searchInResource(this._sourceCode.parentFrame.id, this._sourceCode.url, query, caseSensitive, isRegex, searchResultCallback.bind(this));
+        else if (this._sourceCode instanceof WI.Script)
+            this._sourceCode.target.DebuggerAgent.searchInContent(this._sourceCode.id, query, caseSensitive, isRegex, searchResultCallback.bind(this));
         return true;
-    },
+    }
 
-    showGoToLineDialog: function()
+    showGoToLineDialog()
     {
-        if (!this._goToLineDialog) {
-            this._goToLineDialog = new WebInspector.GoToLineDialog;
-            this._goToLineDialog.delegate = this;
-        }
+        if (!this._goToLineDialog)
+            this._goToLineDialog = new WI.GoToLineDialog(this);
 
         this._goToLineDialog.present(this.element);
-    },
+    }
 
-    isGoToLineDialogValueValid: function(goToLineDialog, lineNumber)
+    isDialogRepresentedObjectValid(goToLineDialog, lineNumber)
     {
         return !isNaN(lineNumber) && lineNumber > 0 && lineNumber <= this.lineCount;
-    },
+    }
 
-    goToLineDialogValueWasValidated: function(goToLineDialog, lineNumber)
+    dialogWasDismissedWithRepresentedObject(goToLineDialog, lineNumber)
     {
-        var position = new WebInspector.SourceCodePosition(lineNumber - 1, 0);
-        var range = new WebInspector.TextRange(lineNumber - 1, 0, lineNumber, 0);
+        let position = new WI.SourceCodePosition(lineNumber - 1, 0);
+        let range = new WI.TextRange(lineNumber - 1, 0, lineNumber, 0);
         this.revealPosition(position, range, false, true);
-    },
-
-    goToLineDialogWasDismissed: function()
-    {
-        this.focus();
-    },
+    }
 
-    contentDidChange: function(replacedRanges, newRanges)
+    contentDidChange(replacedRanges, newRanges)
     {
-        WebInspector.TextEditor.prototype.contentDidChange.call(this, replacedRanges, newRanges);
+        super.contentDidChange(replacedRanges, newRanges);
 
         if (this._ignoreContentDidChange > 0)
             return;
@@ -270,27 +291,50 @@ WebInspector.SourceCodeTextEditor.prototype = {
         for (var range of newRanges)
             this._updateEditableMarkers(range);
 
-        if (this._typeTokenAnnotator || this._basicBlockAnnotator) {
+        if (this._basicBlockAnnotator) {
+            this._basicBlockAnnotatorEnabled = false;
+            this._basicBlockAnnotator = null;
+        }
+
+        if (this._typeTokenAnnotator) {
             this._setTypeTokenAnnotatorEnabledState(false);
             this._typeTokenAnnotator = null;
-            this._basicBlockAnnotator = null;
         }
-    },
+    }
 
-    toggleTypeAnnotations: function()
+    toggleTypeAnnotations()
     {
         if (!this._typeTokenAnnotator)
-            return false;
+            return Promise.reject(new Error("TypeTokenAnnotator is not initialized."));
 
         var newActivatedState = !this._typeTokenAnnotator.isActive();
-        if (newActivatedState && this._isProbablyMinified && !this.formatted)
-            this.formatted = true;
+        if (newActivatedState && this._isProbablyMinified && !this.formatted) {
+            return this.updateFormattedState(true).then(() => {
+                this._setTypeTokenAnnotatorEnabledState(newActivatedState);
+            });
+        }
 
         this._setTypeTokenAnnotatorEnabledState(newActivatedState);
-        return newActivatedState;
-    },
+        return Promise.resolve();
+    }
 
-    showPopoverForTypes: function(types, bounds, title)
+    toggleUnexecutedCodeHighlights()
+    {
+        if (!this._basicBlockAnnotator)
+            return Promise.reject(new Error("BasicBlockAnnotator is not initialized."));
+
+        let newActivatedState = !this._basicBlockAnnotator.isActive();
+        if (newActivatedState && this._isProbablyMinified && !this.formatted) {
+            return this.updateFormattedState(true).then(() => {
+                this._basicBlockAnnotatorEnabled = newActivatedState;
+            });
+        }
+
+        this._basicBlockAnnotatorEnabled = newActivatedState;
+        return Promise.resolve();
+    }
+
+    showPopoverForTypes(typeDescription, bounds, title)
     {
         var content = document.createElement("div");
         content.className = "object expandable";
@@ -300,77 +344,87 @@ WebInspector.SourceCodeTextEditor.prototype = {
         titleElement.textContent = title;
         content.appendChild(titleElement);
 
-        var section = new WebInspector.TypePropertiesSection(types);
-        section.expanded = true;
-        section.element.classList.add("body");
-        content.appendChild(section.element);
+        var bodyElement = content.appendChild(document.createElement("div"));
+        bodyElement.className = "body";
+
+        var typeTreeView = new WI.TypeTreeView(typeDescription);
+        bodyElement.appendChild(typeTreeView.element);
 
         this._showPopover(content, bounds);
-    },
+    }
 
     // Protected
 
-    prettyPrint: function(pretty)
+    prettyPrint(pretty)
     {
-        // The annotators must be cleared before pretty printing takes place and resumed 
-        // after so that they clear their annotations in a known state and insert new annotations 
+        // The annotators must be cleared before pretty printing takes place and resumed
+        // after so that they clear their annotations in a known state and insert new annotations
         // in the new state.
-        var shouldResumeTypeTokenAnnotator = this._typeTokenAnnotator && this._typeTokenAnnotator.isActive();
+
         var shouldResumeBasicBlockAnnotator = this._basicBlockAnnotator && this._basicBlockAnnotator.isActive();
-        if (shouldResumeTypeTokenAnnotator || shouldResumeBasicBlockAnnotator)
+        if (shouldResumeBasicBlockAnnotator)
+            this._basicBlockAnnotatorEnabled = false;
+
+        let shouldResumeTypeTokenAnnotator = this._typeTokenAnnotator && this._typeTokenAnnotator.isActive();
+        if (shouldResumeTypeTokenAnnotator)
             this._setTypeTokenAnnotatorEnabledState(false);
 
-        WebInspector.TextEditor.prototype.prettyPrint.call(this, pretty);
+        return super.prettyPrint(pretty).then(() => {
+            if (pretty || !this._isProbablyMinified) {
+                if (shouldResumeBasicBlockAnnotator)
+                    this._basicBlockAnnotatorEnabled = true;
+
+                if (shouldResumeTypeTokenAnnotator)
+                    this._setTypeTokenAnnotatorEnabledState(true);
+            } else {
+                console.assert(!pretty && this._isProbablyMinified);
+                if (this._basicBlockAnnotator)
+                    this._basicBlockAnnotatorEnabled = false;
 
-        if (pretty || !this._isProbablyMinified) {
-            if (shouldResumeTypeTokenAnnotator || shouldResumeBasicBlockAnnotator)
-                this._setTypeTokenAnnotatorEnabledState(true);
-        } else {
-            console.assert(!pretty && this._isProbablyMinified);
-            if (this._typeTokenAnnotator || this._basicBlockAnnotator)
                 this._setTypeTokenAnnotatorEnabledState(false);
-        }
-    },
+            }
+        });
+    }
 
     // Private
 
-    _unformattedLineInfoForEditorLineInfo: function(lineInfo)
+    _unformattedLineInfoForEditorLineInfo(lineInfo)
     {
         if (this.formatterSourceMap)
             return this.formatterSourceMap.formattedToOriginal(lineInfo.lineNumber, lineInfo.columnNumber);
         return lineInfo;
-    },
+    }
 
-    _sourceCodeLocationForEditorPosition: function(position)
+    _sourceCodeLocationForEditorPosition(position)
     {
         var lineInfo = {lineNumber: position.line, columnNumber: position.ch};
         var unformattedLineInfo = this._unformattedLineInfoForEditorLineInfo(lineInfo);
         return this.sourceCode.createSourceCodeLocation(unformattedLineInfo.lineNumber, unformattedLineInfo.columnNumber);
-    },
+    }
 
-    _editorLineInfoForSourceCodeLocation: function(sourceCodeLocation)
+    _editorLineInfoForSourceCodeLocation(sourceCodeLocation)
     {
-        if (this._sourceCode instanceof WebInspector.SourceMapResource)
+        if (this._sourceCode instanceof WI.SourceMapResource)
             return {lineNumber: sourceCodeLocation.displayLineNumber, columnNumber: sourceCodeLocation.displayColumnNumber};
         return {lineNumber: sourceCodeLocation.formattedLineNumber, columnNumber: sourceCodeLocation.formattedColumnNumber};
-    },
+    }
 
-    _breakpointForEditorLineInfo: function(lineInfo)
+    _breakpointForEditorLineInfo(lineInfo)
     {
         if (!this._breakpointMap[lineInfo.lineNumber])
             return null;
         return this._breakpointMap[lineInfo.lineNumber][lineInfo.columnNumber];
-    },
+    }
 
-    _addBreakpointWithEditorLineInfo: function(breakpoint, lineInfo)
+    _addBreakpointWithEditorLineInfo(breakpoint, lineInfo)
     {
         if (!this._breakpointMap[lineInfo.lineNumber])
             this._breakpointMap[lineInfo.lineNumber] = {};
 
         this._breakpointMap[lineInfo.lineNumber][lineInfo.columnNumber] = breakpoint;
-    },
+    }
 
-    _removeBreakpointWithEditorLineInfo: function(breakpoint, lineInfo)
+    _removeBreakpointWithEditorLineInfo(breakpoint, lineInfo)
     {
         console.assert(breakpoint === this._breakpointMap[lineInfo.lineNumber][lineInfo.columnNumber]);
 
@@ -378,107 +432,108 @@ WebInspector.SourceCodeTextEditor.prototype = {
 
         if (isEmptyObject(this._breakpointMap[lineInfo.lineNumber]))
             delete this._breakpointMap[lineInfo.lineNumber];
-    },
+    }
 
-    _contentWillPopulate: function(content)
+    _populateWithContent(content)
     {
-        this.dispatchEventToListeners(WebInspector.SourceCodeTextEditor.Event.ContentWillPopulate);
+        content = content || "";
 
-        // We only do the rest of this work before the first populate.
-        if (this._contentPopulated)
+        this._prepareEditorForInitialContent(content);
+
+        // If we can auto format, format the TextEditor before showing it.
+        if (this._autoFormat) {
+            console.assert(!this.formatted);
+            this._autoFormat = false;
+            this.deferReveal = true;
+            this.string = content;
+            this.deferReveal = false;
+            this.updateFormattedState(true).then(() => {
+                this._proceedPopulateWithContent(this.string);
+            });
             return;
+        }
 
-        if (this._supportsDebugging) {
-            this._breakpointMap = {};
+        this._proceedPopulateWithContent(content);
+    }
 
-            var breakpoints = WebInspector.debuggerManager.breakpointsForSourceCode(this._sourceCode);
-            for (var i = 0; i < breakpoints.length; ++i) {
-                var breakpoint = breakpoints[i];
-                console.assert(this._matchesBreakpoint(breakpoint));
-                var lineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
-                this._addBreakpointWithEditorLineInfo(breakpoint, lineInfo);
-                this.setBreakpointInfoForLineAndColumn(lineInfo.lineNumber, lineInfo.columnNumber, this._breakpointInfoForBreakpoint(breakpoint));
-            }
-        }
+    _proceedPopulateWithContent(content)
+    {
+        this.dispatchEventToListeners(WI.SourceCodeTextEditor.Event.ContentWillPopulate);
 
-        if (this._sourceCode instanceof WebInspector.Resource)
-            this.mimeType = this._sourceCode.syntheticMIMEType;
-        else if (this._sourceCode instanceof WebInspector.Script)
-            this.mimeType = "text/javascript";
+        this.string = content;
 
-        // Automatically format the content if it looks minified and it can be formatted.
-        console.assert(!this.formatted);
-        if (this.canBeFormatted()) {
-            var lastNewlineIndex = 0;
-            while (true) {
-                var nextNewlineIndex = content.indexOf("\n", lastNewlineIndex);
-                if (nextNewlineIndex === -1) {
-                    if (content.length - lastNewlineIndex > WebInspector.SourceCodeTextEditor.AutoFormatMinimumLineLength) {
-                        this.autoFormat = true;
-                        this._isProbablyMinified = true;
-                    }
-                    break;
-                }
+        this._createBasicBlockAnnotator();
+        if (WI.settings.enableControlFlowProfiler.value && this._basicBlockAnnotator)
+            this._basicBlockAnnotatorEnabled = true;
 
-                if (nextNewlineIndex - lastNewlineIndex > WebInspector.SourceCodeTextEditor.AutoFormatMinimumLineLength) {
-                    this.autoFormat = true;
-                    this._isProbablyMinified = true;
-                    break;
-                }
+        this._createTypeTokenAnnotator();
+        if (WI.settings.showJavaScriptTypeInformation.value)
+            this._setTypeTokenAnnotatorEnabledState(true);
 
-                lastNewlineIndex = nextNewlineIndex + 1;
-            }
-        }
-    },
+        this._contentDidPopulate();
+    }
 
-    _contentDidPopulate: function()
+    _contentDidPopulate()
     {
         this._contentPopulated = true;
 
-        this.dispatchEventToListeners(WebInspector.SourceCodeTextEditor.Event.ContentDidPopulate);
+        this.dispatchEventToListeners(WI.SourceCodeTextEditor.Event.ContentDidPopulate);
 
         // We add the issues each time content is populated. This is needed because lines might not exist
-        // if we tried added them before when the full content wasn't avaiable. (When populating with
+        // if we tried added them before when the full content wasn't available. (When populating with
         // partial script content this can be called multiple times.)
 
-        this._issuesLineNumberMap = {};
-
-        var issues = WebInspector.issueManager.issuesForSourceCode(this._sourceCode);
-        for (var i = 0; i < issues.length; ++i) {
-            var issue = issues[i];
-            console.assert(this._matchesIssue(issue));
-            this._addIssue(issue);
-        }
+        this._reinsertAllIssues();
+        this._reinsertAllThreadIndicators();
 
         this._updateEditableMarkers();
-    },
+    }
 
-    _populateWithContent: function(content)
+    _prepareEditorForInitialContent(content)
     {
-        content = content || "";
-
-        this._contentWillPopulate(content);
-        this.string = content;
+        // Only do this work before the first populate.
+        if (this._contentPopulated)
+            return;
 
-        this._makeTypeTokenAnnotator();
-        this._makeBasicBlockAnnotator();
+        if (this._supportsDebugging) {
+            this._breakpointMap = {};
 
-        if (WebInspector.showJavaScriptTypeInformationSetting.value) {
-            if (this._basicBlockAnnotator || this._typeTokenAnnotator)
-                this._setTypeTokenAnnotatorEnabledState(true);
+            for (let breakpoint of WI.debuggerManager.breakpointsForSourceCode(this._sourceCode)) {
+                console.assert(this._matchesBreakpoint(breakpoint));
+                var lineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
+                this._addBreakpointWithEditorLineInfo(breakpoint, lineInfo);
+                this.setBreakpointInfoForLineAndColumn(lineInfo.lineNumber, lineInfo.columnNumber, this._breakpointInfoForBreakpoint(breakpoint));
+            }
         }
 
-        this._contentDidPopulate();
-    },
+        if (this._sourceCode instanceof WI.Resource)
+            this.mimeType = this._sourceCode.syntheticMIMEType;
+        else if (this._sourceCode instanceof WI.Script)
+            this.mimeType = "text/javascript";
+        else if (this._sourceCode instanceof WI.CSSStyleSheet)
+            this.mimeType = "text/css";
 
-    _contentAvailable: function(parameters)
+        // Decide to automatically format the content if it looks minified and it can be formatted.
+        console.assert(!this.formatted);
+        if (this.canBeFormatted() && isTextLikelyMinified(content)) {
+            this._autoFormat = true;
+            this._isProbablyMinified = true;
+        }
+    }
+
+    _contentAvailable(parameters)
     {
         // Return if resource is not available.
         if (parameters.error)
             return;
 
+        if (parameters.message) {
+            this._showMessage(parameters.message);
+            return;
+        }
+
         var sourceCode = parameters.sourceCode;
-        var content = parameters.content;
+        var content = sourceCode.content;
         var base64Encoded = parameters.base64Encoded;
 
         console.assert(sourceCode === this._sourceCode);
@@ -491,24 +546,35 @@ WebInspector.SourceCodeTextEditor.prototype = {
         this._fullContentPopulated = true;
         this._invalidLineNumbers = {};
 
+        // If we had partial content (such as inline script content) before we had full content, we
+        // will want to re-restore the revealed position now that we are populating with full content.
+        this.repeatReveal = !!this.string;
+
         this._populateWithContent(content);
-    },
 
-    _breakpointStatusDidChange: function(event)
+        this.repeatReveal = false;
+    }
+
+    _showMessage(message)
+    {
+        this.element.removeChildren();
+        this.element.appendChild(WI.createMessageTextView(message));
+    }
+
+    _breakpointStatusDidChange(event)
     {
         this._updateBreakpointStatus(event.target);
-    },
+    }
 
-    _breakpointsEnabledDidChange: function()
+    _breakpointsEnabledDidChange()
     {
         console.assert(this._supportsDebugging);
 
-        var breakpoints = WebInspector.debuggerManager.breakpointsForSourceCode(this._sourceCode);
-        for (var breakpoint of breakpoints)
+        for (let breakpoint of WI.debuggerManager.breakpointsForSourceCode(this._sourceCode))
             this._updateBreakpointStatus(breakpoint);
-    },
+    }
 
-    _updateBreakpointStatus: function(breakpoint)
+    _updateBreakpointStatus(breakpoint)
     {
         console.assert(this._supportsDebugging);
 
@@ -520,9 +586,9 @@ WebInspector.SourceCodeTextEditor.prototype = {
 
         var lineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
         this.setBreakpointInfoForLineAndColumn(lineInfo.lineNumber, lineInfo.columnNumber, this._breakpointInfoForBreakpoint(breakpoint));
-    },
+    }
 
-    _updateBreakpointLocation: function(event)
+    _updateBreakpointLocation(event)
     {
         console.assert(this._supportsDebugging);
 
@@ -541,7 +607,7 @@ WebInspector.SourceCodeTextEditor.prototype = {
 
         var sourceCodeLocation = breakpoint.sourceCodeLocation;
 
-        if (this._sourceCode instanceof WebInspector.SourceMapResource) {
+        if (this._sourceCode instanceof WI.SourceMapResource) {
             // Update our breakpoint location if the display location changed.
             if (sourceCodeLocation.displaySourceCode !== this._sourceCode)
                 return;
@@ -566,9 +632,9 @@ WebInspector.SourceCodeTextEditor.prototype = {
 
         this._removeBreakpointWithEditorLineInfo(breakpoint, oldLineInfo);
         this._addBreakpointWithEditorLineInfo(breakpoint, newLineInfo);
-    },
+    }
 
-    _breakpointAdded: function(event)
+    _breakpointAdded(event)
     {
         console.assert(this._supportsDebugging);
 
@@ -585,9 +651,9 @@ WebInspector.SourceCodeTextEditor.prototype = {
         var lineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
         this._addBreakpointWithEditorLineInfo(breakpoint, lineInfo);
         this.setBreakpointInfoForLineAndColumn(lineInfo.lineNumber, lineInfo.columnNumber, this._breakpointInfoForBreakpoint(breakpoint));
-    },
+    }
 
-    _breakpointRemoved: function(event)
+    _breakpointRemoved(event)
     {
         console.assert(this._supportsDebugging);
 
@@ -604,68 +670,206 @@ WebInspector.SourceCodeTextEditor.prototype = {
         var lineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
         this._removeBreakpointWithEditorLineInfo(breakpoint, lineInfo);
         this.setBreakpointInfoForLineAndColumn(lineInfo.lineNumber, lineInfo.columnNumber, null);
-    },
+    }
+
+    _targetAdded(event)
+    {
+        if (WI.targets.length === 2)
+            this._reinsertAllThreadIndicators();
+    }
+
+    _targetRemoved(event)
+    {
+        if (WI.targets.length === 1) {
+            // Back to one thread, remove thread indicators.
+            this._reinsertAllThreadIndicators();
+            return;
+        }
+
+        let target = event.data.target;
+        this._removeThreadIndicatorForTarget(target);
+    }
+
+    _callFramesDidChange(event)
+    {
+        if (WI.targets.length === 1)
+            return;
+
+        let target = event.data.target;
+        this._removeThreadIndicatorForTarget(target);
+        this._addThreadIndicatorForTarget(target);
+    }
 
-    _activeCallFrameDidChange: function()
+    _addThreadIndicatorForTarget(target)
+    {
+        let targetData = WI.debuggerManager.dataForTarget(target);
+        let topCallFrame = targetData.callFrames[0];
+        if (!topCallFrame)
+            return;
+
+        let sourceCodeLocation = topCallFrame.sourceCodeLocation;
+        console.assert(sourceCodeLocation, "Expected source code location to place thread indicator.");
+        if (!sourceCodeLocation)
+            return;
+
+        if (!this._looselyMatchesSourceCodeLocation(sourceCodeLocation))
+            return;
+
+        let lineNumberWithIndicator = sourceCodeLocation.formattedLineNumber;
+        this._threadTargetMap.set(target, lineNumberWithIndicator);
+
+        let threads = this._threadLineNumberMap.get(lineNumberWithIndicator);
+        if (!threads) {
+            threads = [];
+            this._threadLineNumberMap.set(lineNumberWithIndicator, threads);
+        }
+        threads.push(target);
+
+        let widget = this._threadIndicatorWidgetForLine(target, lineNumberWithIndicator);
+        this._updateThreadIndicatorWidget(widget, threads);
+
+        this.addStyleClassToLine(lineNumberWithIndicator, "thread-indicator");
+    }
+
+    _removeThreadIndicatorForTarget(target)
+    {
+        let lineNumberWithIndicator = this._threadTargetMap.take(target);
+        if (lineNumberWithIndicator === undefined)
+            return;
+
+        let threads = this._threadLineNumberMap.get(lineNumberWithIndicator);
+        threads.remove(target);
+        if (threads.length) {
+            let widget = this._threadWidgetMap.get(lineNumberWithIndicator);
+            this._updateThreadIndicatorWidget(widget, threads);
+            return;
+        }
+
+        this._threadLineNumberMap.delete(lineNumberWithIndicator);
+
+        let widget = this._threadWidgetMap.take(lineNumberWithIndicator);
+        if (widget)
+            widget.clear();
+
+        this.removeStyleClassFromLine(lineNumberWithIndicator, "thread-indicator");
+    }
+
+    _threadIndicatorWidgetForLine(target, lineNumber)
+    {
+        let widget = this._threadWidgetMap.get(lineNumber);
+        if (widget)
+            return widget;
+
+        widget = this.createWidgetForLine(lineNumber);
+        if (!widget)
+            return null;
+
+        let widgetElement = widget.widgetElement;
+        widgetElement.classList.add("line-indicator-widget", "thread-widget", "inline");
+        widgetElement.addEventListener("click", this._handleThreadIndicatorWidgetClick.bind(this, widget, lineNumber));
+
+        this._threadWidgetMap.set(lineNumber, widget);
+
+        return widget;
+    }
+
+    _updateThreadIndicatorWidget(widget, threads)
+    {
+        if (!widget)
+            return;
+
+        console.assert(WI.targets.length > 1);
+
+        let widgetElement = widget.widgetElement;
+        widgetElement.removeChildren();
+
+        widget[WI.SourceCodeTextEditor.WidgetContainsMultipleThreadsSymbol] = threads.length > 1;
+
+        if (widgetElement.classList.contains("inline") || threads.length === 1) {
+            let textElement = widgetElement.appendChild(document.createElement("span"));
+            textElement.className = "text";
+            textElement.textContent = threads.length === 1 ? threads[0].displayName : WI.UIString("%d Threads").format(threads.length);
+        } else {
+            for (let target of threads) {
+                let textElement = widgetElement.appendChild(document.createElement("span"));
+                textElement.className = "text";
+                textElement.textContent = target.displayName;
+
+                widgetElement.appendChild(document.createElement("br"));
+            }
+        }
+
+        widget.update();
+    }
+
+    _handleThreadIndicatorWidgetClick(widget, lineNumber, event)
+    {
+        if (!this._isWidgetToggleable(widget))
+            return;
+
+        widget.widgetElement.classList.toggle("inline");
+
+        let threads = this._threadLineNumberMap.get(lineNumber);
+        this._updateThreadIndicatorWidget(widget, threads);
+    }
+
+    _activeCallFrameDidChange()
     {
         console.assert(this._supportsDebugging);
 
         if (this._activeCallFrameSourceCodeLocation) {
-            this._activeCallFrameSourceCodeLocation.removeEventListener(WebInspector.SourceCodeLocation.Event.LocationChanged, this._activeCallFrameSourceCodeLocationChanged, this);
-            delete this._activeCallFrameSourceCodeLocation;
+            this._activeCallFrameSourceCodeLocation.removeEventListener(WI.SourceCodeLocation.Event.LocationChanged, this._activeCallFrameSourceCodeLocationChanged, this);
+            this._activeCallFrameSourceCodeLocation = null;
         }
 
-        var activeCallFrame = WebInspector.debuggerManager.activeCallFrame;
+        let activeCallFrame = WI.debuggerManager.activeCallFrame;
         if (!activeCallFrame || !this._matchesSourceCodeLocation(activeCallFrame.sourceCodeLocation)) {
-            this.executionLineNumber = NaN;
-            this.executionColumnNumber = NaN;
+            this.setExecutionLineAndColumn(NaN, NaN);
             return;
         }
 
         this._dismissPopover();
 
         this._activeCallFrameSourceCodeLocation = activeCallFrame.sourceCodeLocation;
-        this._activeCallFrameSourceCodeLocation.addEventListener(WebInspector.SourceCodeLocation.Event.LocationChanged, this._activeCallFrameSourceCodeLocationChanged, this);
+        this._activeCallFrameSourceCodeLocation.addEventListener(WI.SourceCodeLocation.Event.LocationChanged, this._activeCallFrameSourceCodeLocationChanged, this);
 
         // Don't return early if the line number didn't change. The execution state still
         // could have changed (e.g. continuing in a loop with a breakpoint inside).
 
-        var lineInfo = this._editorLineInfoForSourceCodeLocation(activeCallFrame.sourceCodeLocation);
-        this.executionLineNumber = lineInfo.lineNumber;
-        this.executionColumnNumber = lineInfo.columnNumber;
+        let lineInfo = this._editorLineInfoForSourceCodeLocation(activeCallFrame.sourceCodeLocation);
+        this.setExecutionLineAndColumn(lineInfo.lineNumber, lineInfo.columnNumber);
 
         // If we have full content or this source code isn't a Resource we can return early.
         // Script source code populates from the request started in the constructor.
-        if (this._fullContentPopulated || !(this._sourceCode instanceof WebInspector.Resource) || this._requestingScriptContent)
+        if (this._fullContentPopulated || !(this._sourceCode instanceof WI.Resource) || this._requestingScriptContent)
             return;
 
         // Since we are paused in the debugger we need to show some content, and since the Resource
         // content hasn't populated yet we need to populate with content from the Scripts by URL.
         // Document resources will attempt to populate the scripts as inline (in <script> tags.)
         // Other resources are assumed to be full scripts (JavaScript resources).
-        if (this._sourceCode.type === WebInspector.Resource.Type.Document)
+        if (this._sourceCode.type === WI.Resource.Type.Document)
             this._populateWithInlineScriptContent();
         else
             this._populateWithScriptContent();
-    },
+    }
 
-    _activeCallFrameSourceCodeLocationChanged: function(event)
+    _activeCallFrameSourceCodeLocationChanged(event)
     {
         console.assert(!isNaN(this.executionLineNumber));
         if (isNaN(this.executionLineNumber))
             return;
 
-        console.assert(WebInspector.debuggerManager.activeCallFrame);
-        console.assert(this._activeCallFrameSourceCodeLocation === WebInspector.debuggerManager.activeCallFrame.sourceCodeLocation);
+        console.assert(WI.debuggerManager.activeCallFrame);
+        console.assert(this._activeCallFrameSourceCodeLocation === WI.debuggerManager.activeCallFrame.sourceCodeLocation);
 
         var lineInfo = this._editorLineInfoForSourceCodeLocation(this._activeCallFrameSourceCodeLocation);
-        this.executionLineNumber = lineInfo.lineNumber;
-        this.executionColumnNumber = lineInfo.columnNumber;
-    },
+        this.setExecutionLineAndColumn(lineInfo.lineNumber, lineInfo.columnNumber);
+    }
 
-    _populateWithInlineScriptContent: function()
+    _populateWithInlineScriptContent()
     {
-        console.assert(this._sourceCode instanceof WebInspector.Resource);
+        console.assert(this._sourceCode instanceof WI.Resource);
         console.assert(!this._fullContentPopulated);
         console.assert(!this._requestingScriptContent);
 
@@ -688,14 +892,14 @@ WebInspector.SourceCodeTextEditor.prototype = {
             if (--pendingRequestCount)
                 return;
 
-            delete this._requestingScriptContent;
+            this._requestingScriptContent = false;
 
             // Abort if the full content populated while waiting for these async callbacks.
             if (this._fullContentPopulated)
                 return;
 
-            const scriptOpenTag = "<script>";
-            const scriptCloseTag = "</script>";
+            var scriptOpenTag = "<script>";
+            var scriptCloseTag = "</script>";
 
             var content = "";
             var lineNumber = 0;
@@ -733,11 +937,11 @@ WebInspector.SourceCodeTextEditor.prototype = {
         var boundScriptContentAvailable = scriptContentAvailable.bind(this);
         for (var i = 0; i < scripts.length; ++i)
             scripts[i].requestContent().then(boundScriptContentAvailable);
-    },
+    }
 
-    _populateWithScriptContent: function()
+    _populateWithScriptContent()
     {
-        console.assert(this._sourceCode instanceof WebInspector.Resource);
+        console.assert(this._sourceCode instanceof WI.Resource);
         console.assert(!this._fullContentPopulated);
         console.assert(!this._requestingScriptContent);
 
@@ -753,7 +957,7 @@ WebInspector.SourceCodeTextEditor.prototype = {
         function scriptContentAvailable(parameters)
         {
             var content = parameters.content;
-            delete this._requestingScriptContent;
+            this._requestingScriptContent = false;
 
             // Abort if the full content populated while waiting for this async callback.
             if (this._fullContentPopulated)
@@ -768,129 +972,271 @@ WebInspector.SourceCodeTextEditor.prototype = {
         this._requestingScriptContent = true;
 
         scripts[0].requestContent().then(scriptContentAvailable.bind(this));
-    },
+    }
 
-    _matchesSourceCodeLocation: function(sourceCodeLocation)
+    _looselyMatchesSourceCodeLocation(sourceCodeLocation)
     {
-        if (this._sourceCode instanceof WebInspector.SourceMapResource)
+        if (this._sourceCode instanceof WI.SourceMapResource)
             return sourceCodeLocation.displaySourceCode === this._sourceCode;
-        if (this._sourceCode instanceof WebInspector.Resource)
+        if (this._sourceCode instanceof WI.Resource || this._sourceCode instanceof WI.Script || this._sourceCode instanceof WI.CSSStyleSheet)
             return sourceCodeLocation.sourceCode.url === this._sourceCode.url;
-        if (this._sourceCode instanceof WebInspector.Script)
+        return false;
+    }
+
+    _matchesSourceCodeLocation(sourceCodeLocation)
+    {
+        if (this._sourceCode instanceof WI.SourceMapResource)
+            return sourceCodeLocation.displaySourceCode === this._sourceCode;
+        if (this._sourceCode instanceof WI.Resource || this._sourceCode instanceof WI.CSSStyleSheet)
+            return sourceCodeLocation.sourceCode.url === this._sourceCode.url;
+        if (this._sourceCode instanceof WI.Script)
             return sourceCodeLocation.sourceCode === this._sourceCode;
         return false;
-    },
+    }
+
+    _matchesBreakpoint(breakpoint)
+    {
+        console.assert(this._supportsDebugging);
+        if (this._sourceCode instanceof WI.SourceMapResource)
+            return breakpoint.sourceCodeLocation.displaySourceCode === this._sourceCode;
+        if (this._sourceCode instanceof WI.Resource)
+            return breakpoint.contentIdentifier === this._sourceCode.contentIdentifier;
+        if (this._sourceCode instanceof WI.Script)
+            return breakpoint.contentIdentifier === this._sourceCode.contentIdentifier || breakpoint.scriptIdentifier === this._sourceCode.id;
+        return false;
+    }
+
+    _issueWasAdded(event)
+    {
+        var issue = event.data.issue;
+        if (!WI.ConsoleManager.issueMatchSourceCode(issue, this._sourceCode))
+            return;
+
+        this._addIssue(issue);
+    }
+
+    _addIssue(issue)
+    {
+        var sourceCodeLocation = issue.sourceCodeLocation;
+        console.assert(sourceCodeLocation, "Expected source code location to place issue.");
+        if (!sourceCodeLocation)
+            return;
+
+        var lineNumber = sourceCodeLocation.formattedLineNumber;
+
+        var lineNumberIssues = this._issuesLineNumberMap.get(lineNumber);
+        if (!lineNumberIssues) {
+            lineNumberIssues = [];
+            this._issuesLineNumberMap.set(lineNumber, lineNumberIssues);
+        }
+
+        // Avoid displaying duplicate issues on the same line.
+        for (var existingIssue of lineNumberIssues) {
+            if (existingIssue.sourceCodeLocation.columnNumber === sourceCodeLocation.columnNumber && existingIssue.text === issue.text)
+                return;
+        }
+
+        lineNumberIssues.push(issue);
+
+        if (issue.level === WI.IssueMessage.Level.Error)
+            this.addStyleClassToLine(lineNumber, WI.SourceCodeTextEditor.LineErrorStyleClassName);
+        else if (issue.level === WI.IssueMessage.Level.Warning)
+            this.addStyleClassToLine(lineNumber, WI.SourceCodeTextEditor.LineWarningStyleClassName);
+        else
+            console.error("Unknown issue level");
+
+        var widget = this._issueWidgetForLine(lineNumber);
+        if (widget) {
+            if (issue.level === WI.IssueMessage.Level.Error)
+                widget.widgetElement.classList.add(WI.SourceCodeTextEditor.LineErrorStyleClassName);
+            else if (issue.level === WI.IssueMessage.Level.Warning)
+                widget.widgetElement.classList.add(WI.SourceCodeTextEditor.LineWarningStyleClassName);
+
+            this._updateIssueWidgetForIssues(widget, lineNumberIssues);
+        }
+    }
+
+    _issueWidgetForLine(lineNumber)
+    {
+        var widget = this._widgetMap.get(lineNumber);
+        if (widget)
+            return widget;
+
+        widget = this.createWidgetForLine(lineNumber);
+        if (!widget)
+            return null;
+
+        var widgetElement = widget.widgetElement;
+        widgetElement.classList.add("line-indicator-widget", "issue-widget", "inline");
+        widgetElement.addEventListener("click", this._handleWidgetClick.bind(this, widget, lineNumber));
+
+        this._widgetMap.set(lineNumber, widget);
+
+        return widget;
+    }
+
+    _iconClassNameForIssueLevel(level)
+    {
+        if (level === WI.IssueMessage.Level.Warning)
+            return "icon-warning";
+
+        console.assert(level === WI.IssueMessage.Level.Error);
+        return "icon-error";
+    }
+
+    _updateIssueWidgetForIssues(widget, issues)
+    {
+        var widgetElement = widget.widgetElement;
+        widgetElement.removeChildren();
+
+        if (widgetElement.classList.contains("inline") || issues.length === 1) {
+            var iconElement = widgetElement.appendChild(document.createElement("span"));
+            iconElement.className = "icon";
+
+            var textElement = widgetElement.appendChild(document.createElement("span"));
+            textElement.className = "text";
+
+            if (issues.length === 1) {
+                iconElement.classList.add(this._iconClassNameForIssueLevel(issues[0].level));
+                textElement.textContent = issues[0].text;
+            } else {
+                var errorsCount = 0;
+                var warningsCount = 0;
+                for (var issue of issues) {
+                    if (issue.level === WI.IssueMessage.Level.Error)
+                        ++errorsCount;
+                    else if (issue.level === WI.IssueMessage.Level.Warning)
+                        ++warningsCount;
+                }
+
+                if (warningsCount && errorsCount) {
+                    iconElement.classList.add(this._iconClassNameForIssueLevel(issue.level));
+                    textElement.textContent = WI.UIString("%d Errors, %d Warnings").format(errorsCount, warningsCount);
+                } else if (errorsCount) {
+                    iconElement.classList.add(this._iconClassNameForIssueLevel(issue.level));
+                    textElement.textContent = WI.UIString("%d Errors").format(errorsCount);
+                } else if (warningsCount) {
+                    iconElement.classList.add(this._iconClassNameForIssueLevel(issue.level));
+                    textElement.textContent = WI.UIString("%d Warnings").format(warningsCount);
+                }
+
+                widget[WI.SourceCodeTextEditor.WidgetContainsMultipleIssuesSymbol] = true;
+            }
+        } else {
+            for (var issue of issues) {
+                var iconElement = widgetElement.appendChild(document.createElement("span"));
+                iconElement.className = "icon";
+                iconElement.classList.add(this._iconClassNameForIssueLevel(issue.level));
+
+                var textElement = widgetElement.appendChild(document.createElement("span"));
+                textElement.className = "text";
+                textElement.textContent = issue.text;
+
+                widgetElement.appendChild(document.createElement("br"));
+            }
+        }
+
+        widget.update();
+    }
+
+    _isWidgetToggleable(widget)
+    {
+        if (widget[WI.SourceCodeTextEditor.WidgetContainsMultipleIssuesSymbol])
+            return true;
+
+        if (widget[WI.SourceCodeTextEditor.WidgetContainsMultipleThreadsSymbol])
+            return true;
+
+        if (!widget.widgetElement.classList.contains("inline"))
+            return true;
 
-    _matchesBreakpoint: function(breakpoint)
-    {
-        console.assert(this._supportsDebugging);
-        if (this._sourceCode instanceof WebInspector.SourceMapResource)
-            return breakpoint.sourceCodeLocation.displaySourceCode === this._sourceCode;
-        if (this._sourceCode instanceof WebInspector.Resource)
-            return breakpoint.url === this._sourceCode.url;
-        if (this._sourceCode instanceof WebInspector.Script)
-            return breakpoint.url === this._sourceCode.url || breakpoint.scriptIdentifier === this._sourceCode.id;
-        return false;
-    },
+        var textElement = widget.widgetElement.lastChild;
+        if (textElement.offsetWidth !== textElement.scrollWidth)
+            return true;
 
-    _matchesIssue: function(issue)
-    {
-        if (this._sourceCode instanceof WebInspector.Resource)
-            return issue.url === this._sourceCode.url;
-        // FIXME: Support issues for Scripts based on id, not only by URL.
-        if (this._sourceCode instanceof WebInspector.Script)
-            return issue.url === this._sourceCode.url;
         return false;
-    },
+    }
 
-    _issueWasAdded: function(event)
+    _handleWidgetClick(widget, lineNumber, event)
     {
-        var issue = event.data.issue;
-        if (!this._matchesIssue(issue))
+        if (!this._isWidgetToggleable(widget))
             return;
 
-        this._addIssue(issue);
-    },
-
-    _addIssue: function(issue)
-    {
-        var lineNumberIssues = this._issuesLineNumberMap[issue.lineNumber];
-        if (!lineNumberIssues)
-            lineNumberIssues = this._issuesLineNumberMap[issue.lineNumber] = [];
-
-        lineNumberIssues.push(issue);
-
-        if (issue.level === WebInspector.IssueMessage.Level.Error)
-            this.addStyleClassToLine(issue.lineNumber, WebInspector.SourceCodeTextEditor.LineErrorStyleClassName);
-        else if (issue.level === WebInspector.IssueMessage.Level.Warning)
-            this.addStyleClassToLine(issue.lineNumber, WebInspector.SourceCodeTextEditor.LineWarningStyleClassName);
-        else
-            console.error("Unknown issue level");
+        widget.widgetElement.classList.toggle("inline");
 
-        // FIXME <rdar://problem/10854857>: Show the issue message on the line as a bubble.
-    },
+        var lineNumberIssues = this._issuesLineNumberMap.get(lineNumber);
+        this._updateIssueWidgetForIssues(widget, lineNumberIssues);
+    }
 
-    _breakpointInfoForBreakpoint: function(breakpoint)
+    _breakpointInfoForBreakpoint(breakpoint)
     {
         return {resolved: breakpoint.resolved, disabled: breakpoint.disabled, autoContinue: breakpoint.autoContinue};
-    },
+    }
 
     get _supportsDebugging()
     {
-        if (this._sourceCode instanceof WebInspector.Resource)
-            return this._sourceCode.type === WebInspector.Resource.Type.Document || this._sourceCode.type === WebInspector.Resource.Type.Script;
-        if (this._sourceCode instanceof WebInspector.Script)
-            return true;
+        if (this._sourceCode instanceof WI.Resource)
+            return this._sourceCode.type === WI.Resource.Type.Document || this._sourceCode.type === WI.Resource.Type.Script;
+        if (this._sourceCode instanceof WI.Script)
+            return !(this._sourceCode instanceof WI.LocalScript);
         return false;
-    },
+    }
 
     // TextEditor Delegate
 
-    textEditorBaseURL: function(textEditor)
+    textEditorBaseURL(textEditor)
     {
         return this._sourceCode.url;
-    },
+    }
+
+    textEditorScriptSourceType(textEditor)
+    {
+        let script = this._getAssociatedScript();
+        return script ? script.sourceType : WI.Script.SourceType.Program;
+    }
 
-    textEditorShouldHideLineNumber: function(textEditor, lineNumber)
+    textEditorShouldHideLineNumber(textEditor, lineNumber)
     {
         return lineNumber in this._invalidLineNumbers;
-    },
+    }
 
-    textEditorGutterContextMenu: function(textEditor, lineNumber, columnNumber, editorBreakpoints, event)
+    textEditorGutterContextMenu(textEditor, lineNumber, columnNumber, editorBreakpoints, event)
     {
         if (!this._supportsDebugging)
             return;
 
         event.preventDefault();
 
-        var contextMenu = new WebInspector.ContextMenu(event);
+        let addBreakpoint = () => {
+            let data = this.textEditorBreakpointAdded(this, lineNumber, columnNumber);
+            this.setBreakpointInfoForLineAndColumn(data.lineNumber, data.columnNumber, data.breakpointInfo);
+        };
+
+        let contextMenu = WI.ContextMenu.createFromEvent(event);
 
         // Paused. Add Continue to Here option only if we have a script identifier for the location.
-        if (WebInspector.debuggerManager.paused) {
-            var editorLineInfo = {lineNumber, columnNumber};
-            var unformattedLineInfo = this._unformattedLineInfoForEditorLineInfo(editorLineInfo);
-            var sourceCodeLocation = this._sourceCode.createSourceCodeLocation(unformattedLineInfo.lineNumber, unformattedLineInfo.columnNumber);
+        if (WI.debuggerManager.paused) {
+            let editorLineInfo = {lineNumber, columnNumber};
+            let unformattedLineInfo = this._unformattedLineInfoForEditorLineInfo(editorLineInfo);
+            let sourceCodeLocation = this._sourceCode.createSourceCodeLocation(unformattedLineInfo.lineNumber, unformattedLineInfo.columnNumber);
 
-            if (sourceCodeLocation.sourceCode instanceof WebInspector.Script)
-                var script = sourceCodeLocation.sourceCode;
-            else if (sourceCodeLocation.sourceCode instanceof WebInspector.Resource)
-                var script = sourceCodeLocation.sourceCode.scriptForLocation(sourceCodeLocation);
+            let script;
+            if (sourceCodeLocation.sourceCode instanceof WI.Script)
+                script = sourceCodeLocation.sourceCode;
+            else if (sourceCodeLocation.sourceCode instanceof WI.Resource)
+                script = sourceCodeLocation.sourceCode.scriptForLocation(sourceCodeLocation);
 
             if (script) {
-                function continueToLocation()
-                {
-                    WebInspector.debuggerManager.continueToLocation(script.id, sourceCodeLocation.lineNumber, sourceCodeLocation.columnNumber);
-                }
-
-                contextMenu.appendItem(WebInspector.UIString("Continue to Here"), continueToLocation);
+                contextMenu.appendItem(WI.UIString("Continue to Here"), () => {
+                    WI.debuggerManager.continueToLocation(script, sourceCodeLocation.lineNumber, sourceCodeLocation.columnNumber);
+                });
                 contextMenu.appendSeparator();
             }
         }
 
-        var breakpoints = [];
-        for (var i = 0; i < editorBreakpoints.length; ++i) {
-            var lineInfo = editorBreakpoints[i];
-            var breakpoint = this._breakpointForEditorLineInfo(lineInfo);
+        let breakpoints = [];
+        for (let lineInfo of editorBreakpoints) {
+            let breakpoint = this._breakpointForEditorLineInfo(lineInfo);
             console.assert(breakpoint);
             if (breakpoint)
                 breakpoints.push(breakpoint);
@@ -898,68 +1244,46 @@ WebInspector.SourceCodeTextEditor.prototype = {
 
         // No breakpoints.
         if (!breakpoints.length) {
-            function addBreakpoint()
-            {
-                var data = this.textEditorBreakpointAdded(this, lineNumber, columnNumber);
-                this.setBreakpointInfoForLineAndColumn(data.lineNumber, data.columnNumber, data.breakpointInfo);
-            }
-
-            contextMenu.appendItem(WebInspector.UIString("Add Breakpoint"), addBreakpoint.bind(this));
-            contextMenu.show();
+            contextMenu.appendItem(WI.UIString("Add Breakpoint"), addBreakpoint.bind(this));
             return;
         }
 
         // Single breakpoint.
         if (breakpoints.length === 1) {
-            var breakpoint = breakpoints[0];
-            function revealInSidebar()
-            {
-                WebInspector.debuggerSidebarPanel.show();
-                var treeElement = WebInspector.debuggerSidebarPanel.treeElementForRepresentedObject(breakpoint);
-                if (treeElement)
-                    treeElement.revealAndSelect();
+            WI.breakpointPopoverController.appendContextMenuItems(contextMenu, breakpoints[0], event.target);
+
+            if (WI.settings.experimentalEnableSourcesTab.value) {
+                if (!WI.isShowingSourcesTab()) {
+                    contextMenu.appendSeparator();
+                    contextMenu.appendItem(WI.UIString("Reveal in Sources Tab"), () => {
+                        WI.showSourcesTab({breakpointToSelect: breakpoints[0]});
+                    });
+                }
+            } else {
+                if (!WI.isShowingDebuggerTab()) {
+                    contextMenu.appendSeparator();
+                    contextMenu.appendItem(WI.UIString("Reveal in Debugger Tab"), () => {
+                        WI.showDebuggerTab({breakpointToSelect: breakpoints[0]});
+                    });
+                }
             }
 
-            breakpoint.appendContextMenuItems(contextMenu, event.target);
-            contextMenu.appendSeparator();
-            contextMenu.appendItem(WebInspector.UIString("Reveal in Debugger Navigation Sidebar"), revealInSidebar);
-            contextMenu.show();
             return;
         }
 
-        // Multiple breakpoints.
-        var shouldDisable = false;
-        for (var i = 0; i < breakpoints.length; ++i) {
-            if (!breakpoints[i].disabled) {
-                shouldDisable = true;
-                break;
-            }
-        }
-
-        function removeBreakpoints()
-        {
-            for (var i = 0; i < breakpoints.length; ++i) {
-                var breakpoint = breakpoints[i];
-                if (WebInspector.debuggerManager.isBreakpointRemovable(breakpoint))
-                    WebInspector.debuggerManager.removeBreakpoint(breakpoint);
-            }
-        }
-
-        function toggleBreakpoints()
-        {
-            for (var i = 0; i < breakpoints.length; ++i)
-                breakpoints[i].disabled = shouldDisable;
-        }
+        let shouldDisable = breakpoints.some((breakpoint) => !breakpoint.disabled);
+        contextMenu.appendItem(shouldDisable ? WI.UIString("Disable Breakpoints") : WI.UIString("Enable Breakpoints"), () => {
+            for (let breakpoint of breakpoints)
+                breakpoint.disabled = shouldDisable;
+        });
 
-        if (shouldDisable)
-            contextMenu.appendItem(WebInspector.UIString("Disable Breakpoints"), toggleBreakpoints.bind(this));
-        else
-            contextMenu.appendItem(WebInspector.UIString("Enable Breakpoints"), toggleBreakpoints.bind(this));
-        contextMenu.appendItem(WebInspector.UIString("Delete Breakpoints"), removeBreakpoints.bind(this));
-        contextMenu.show();
-    },
+        contextMenu.appendItem(WI.UIString("Delete Breakpoints"), () => {
+            for (let breakpoint of breakpoints)
+                WI.debuggerManager.removeBreakpoint(breakpoint);
+        });
+    }
 
-    textEditorBreakpointAdded: function(textEditor, lineNumber, columnNumber)
+    textEditorBreakpointAdded(textEditor, lineNumber, columnNumber)
     {
         if (!this._supportsDebugging)
             return null;
@@ -967,17 +1291,14 @@ WebInspector.SourceCodeTextEditor.prototype = {
         var editorLineInfo = {lineNumber, columnNumber};
         var unformattedLineInfo = this._unformattedLineInfoForEditorLineInfo(editorLineInfo);
         var sourceCodeLocation = this._sourceCode.createSourceCodeLocation(unformattedLineInfo.lineNumber, unformattedLineInfo.columnNumber);
-        var breakpoint = new WebInspector.Breakpoint(sourceCodeLocation);
+        var breakpoint = new WI.Breakpoint(sourceCodeLocation);
 
         var lineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
         this._addBreakpointWithEditorLineInfo(breakpoint, lineInfo);
 
         this._ignoreBreakpointAddedBreakpoint = breakpoint;
-
-        var shouldSkipEventDispatch = false;
-        var shouldSpeculativelyResolveBreakpoint = true;
-        WebInspector.debuggerManager.addBreakpoint(breakpoint, shouldSkipEventDispatch, shouldSpeculativelyResolveBreakpoint);
-        delete this._ignoreBreakpointAddedBreakpoint;
+        WI.debuggerManager.addBreakpoint(breakpoint);
+        this._ignoreBreakpointAddedBreakpoint = null;
 
         // Return the more accurate location and breakpoint info.
         return {
@@ -985,9 +1306,9 @@ WebInspector.SourceCodeTextEditor.prototype = {
             lineNumber: lineInfo.lineNumber,
             columnNumber: lineInfo.columnNumber
         };
-    },
+    }
 
-    textEditorBreakpointRemoved: function(textEditor, lineNumber, columnNumber)
+    textEditorBreakpointRemoved(textEditor, lineNumber, columnNumber)
     {
         console.assert(this._supportsDebugging);
         if (!this._supportsDebugging)
@@ -1002,11 +1323,11 @@ WebInspector.SourceCodeTextEditor.prototype = {
         this._removeBreakpointWithEditorLineInfo(breakpoint, lineInfo);
 
         this._ignoreBreakpointRemovedBreakpoint = breakpoint;
-        WebInspector.debuggerManager.removeBreakpoint(breakpoint);
-        delete this._ignoreBreakpointAddedBreakpoint;
-    },
+        WI.debuggerManager.removeBreakpoint(breakpoint);
+        this._ignoreBreakpointRemovedBreakpoint = null;
+    }
 
-    textEditorBreakpointMoved: function(textEditor, oldLineNumber, oldColumnNumber, newLineNumber, newColumnNumber)
+    textEditorBreakpointMoved(textEditor, oldLineNumber, oldColumnNumber, newLineNumber, newColumnNumber)
     {
         console.assert(this._supportsDebugging);
         if (!this._supportsDebugging)
@@ -1024,16 +1345,16 @@ WebInspector.SourceCodeTextEditor.prototype = {
         var unformattedNewLineInfo = this._unformattedLineInfoForEditorLineInfo(newLineInfo);
         this._ignoreLocationUpdateBreakpoint = breakpoint;
         breakpoint.sourceCodeLocation.update(this._sourceCode, unformattedNewLineInfo.lineNumber, unformattedNewLineInfo.columnNumber);
-        delete this._ignoreLocationUpdateBreakpoint;
+        this._ignoreLocationUpdateBreakpoint = null;
 
         var accurateNewLineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
         this._addBreakpointWithEditorLineInfo(breakpoint, accurateNewLineInfo);
 
         if (accurateNewLineInfo.lineNumber !== newLineInfo.lineNumber || accurateNewLineInfo.columnNumber !== newLineInfo.columnNumber)
             this.updateBreakpointLineAndColumn(newLineInfo.lineNumber, newLineInfo.columnNumber, accurateNewLineInfo.lineNumber, accurateNewLineInfo.columnNumber);
-    },
+    }
 
-    textEditorBreakpointClicked: function(textEditor, lineNumber, columnNumber)
+    textEditorBreakpointClicked(textEditor, lineNumber, columnNumber)
     {
         console.assert(this._supportsDebugging);
         if (!this._supportsDebugging)
@@ -1045,54 +1366,221 @@ WebInspector.SourceCodeTextEditor.prototype = {
             return;
 
         breakpoint.cycleToNextMode();
-    },
+    }
 
-    textEditorUpdatedFormatting: function(textEditor)
+    textEditorUpdatedFormatting(textEditor)
     {
         this._ignoreAllBreakpointLocationUpdates = true;
         this._sourceCode.formatterSourceMap = this.formatterSourceMap;
-        delete this._ignoreAllBreakpointLocationUpdates;
+        this._ignoreAllBreakpointLocationUpdates = false;
 
         // Always put the source map on both the Script and Resource if both exist. For example,
         // if this SourceCode is a Resource, then there might also be a Script. In the debugger,
         // the backend identifies call frames with Script line and column information, and the
         // Script needs the formatter source map to produce the proper display line and column.
-        if (this._sourceCode instanceof WebInspector.Resource && !(this._sourceCode instanceof WebInspector.SourceMapResource)) {
+        if (this._sourceCode instanceof WI.Resource && !(this._sourceCode instanceof WI.SourceMapResource)) {
             var scripts = this._sourceCode.scripts;
             for (var i = 0; i < scripts.length; ++i)
                 scripts[i].formatterSourceMap = this.formatterSourceMap;
-        } else if (this._sourceCode instanceof WebInspector.Script) {
+        } else if (this._sourceCode instanceof WI.Script) {
             if (this._sourceCode.resource)
                 this._sourceCode.resource.formatterSourceMap = this.formatterSourceMap;
         }
 
-        // Some breakpoints may have moved, some might not have. Just go through
-        // and remove and reinsert all the breakpoints.
+        this._handleFormatterDidChange();
+    }
 
-        var oldBreakpointMap = this._breakpointMap;
-        this._breakpointMap = {};
+    textEditorExecutionHighlightRange(currentPosition, callback)
+    {
+        let position = this.currentPositionToOriginalPosition(currentPosition);
 
-        for (var lineNumber in oldBreakpointMap) {
-            for (var columnNumber in oldBreakpointMap[lineNumber]) {
-                var breakpoint = oldBreakpointMap[lineNumber][columnNumber];
-                var newLineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
-                this._addBreakpointWithEditorLineInfo(breakpoint, newLineInfo);
-                this.setBreakpointInfoForLineAndColumn(lineNumber, columnNumber, null);
-                this.setBreakpointInfoForLineAndColumn(newLineInfo.lineNumber, newLineInfo.columnNumber, this._breakpointInfoForBreakpoint(breakpoint));
+        let script = this._getAssociatedScript(position);
+        if (!script) {
+            callback(null);
+            return;
+        }
+
+        let {startLine, startColumn} = script.range;
+
+        function toInlineScriptPosition(position) {
+            let columnNumber = position.lineNumber === startLine ? position.columnNumber - startColumn : position.columnNumber;
+            return new WI.SourceCodePosition(position.lineNumber - startLine, columnNumber);
+        }
+
+        function fromInlineScriptPosition(position) {
+            let columnNumber = position.lineNumber ? position.columnNumber : position.columnNumber + startColumn;
+            return new WI.SourceCodePosition(position.lineNumber + startLine, columnNumber);
+        }
+
+        // When returning positions, convert to positions relative to the TextEditor content.
+        let highlightSourceCodeRange = (startPosition, endPosition) => {
+            startPosition = this.originalPositionToCurrentPosition(fromInlineScriptPosition(startPosition));
+            endPosition = this.originalPositionToCurrentPosition(fromInlineScriptPosition(endPosition));
+            callback({startPosition, endPosition});
+        };
+
+        script.requestScriptSyntaxTree((syntaxTree) => {
+            // After requesting the tree, we still might get a null tree from a parse error.
+            if (!syntaxTree) {
+                callback(null);
+                return;
+            }
+
+            // Convert to the position within the inline script before querying the AST.
+            position = toInlineScriptPosition(position);
+            let nodes = syntaxTree.containersOfPosition(position);
+            if (!nodes.length) {
+                callback(null);
+                return;
+            }
+
+            // Find a node starting at this offset.
+            // Avoid highlighting the entire program if this is the start of the first statement.
+            // Special case the assignment expression inside of a for..of and for..in to highlight a larger range.
+            for (let node of nodes) {
+                if (node.startPosition.equals(position) && node.type !== WI.ScriptSyntaxTree.NodeType.Program) {
+                    highlightSourceCodeRange(node.startPosition, node.endPosition);
+                    return;
+                }
+                if (node.type === WI.ScriptSyntaxTree.NodeType.ForInStatement || node.type === WI.ScriptSyntaxTree.NodeType.ForOfStatement) {
+                    if (node.left.startPosition.equals(position)) {
+                        highlightSourceCodeRange(node.left.startPosition, node.right.endPosition);
+                        return;
+                    }
+                }
+                if (node.startPosition.isAfter(position))
+                    break;
+            }
+
+            // Find a node ending at this offset. (Leaving a block).
+            // We check this after ensuring nothing starts with this offset,
+            // as that would be more important.
+            for (let node of nodes) {
+                if (node.endPosition.equals(position)) {
+                    if (node.type === WI.ScriptSyntaxTree.NodeType.BlockStatement) {
+                        // Closing brace of a block, only highlight the closing brace character.
+                        highlightSourceCodeRange(position.offsetColumn(-1), position);
+                        return;
+                    }
+                }
+                if (node.startPosition.isAfter(position))
+                    break;
+            }
+
+            // Find the best container node for this expression.
+            // Sort by the tightest bounds so we can walk from specific to general nodes.
+            nodes.sort((a, b) => {
+                let aLength = a.range[1] - a.range[0];
+                let bLength = b.range[1] - b.range[0];
+                return aLength - bLength;
+            });
+
+            let characterAtPosition = this.getTextInRange(currentPosition, currentPosition.offsetColumn(1));
+            let characterAtPositionIsDotOrBracket = characterAtPosition === "." || characterAtPosition === "[";
+
+            for (let i = 0; i < nodes.length; ++i) {
+                let node = nodes[i];
+
+                // In a function call.
+                if (node.type === WI.ScriptSyntaxTree.NodeType.CallExpression
+                    || node.type === WI.ScriptSyntaxTree.NodeType.NewExpression
+                    || node.type === WI.ScriptSyntaxTree.NodeType.ThrowStatement) {
+                    highlightSourceCodeRange(node.startPosition, node.endPosition);
+                    return;
+                }
+
+                // In the middle of a member expression we want to highlight the best
+                // member expression range. We can end up in the middle when we are
+                // paused inside of a getter and select the parent call frame. For
+                // these cases we may be at a '.' or '[' and we can find the best member
+                // expression from there.
+                //
+                // Examples:
+                //
+                //     foo*.x.y.z => inside x looking at parent call frame => |foo.x|.y.z
+                //     foo.x*.y.z => inside y looking at parent call frame => |foo.x.y|.z
+                //
+                //     foo*["x"]["y"]["z"] => inside x looking at parent call frame => |foo["x"]|["y"]["z"]
+                //     foo["x"]*["y"]["z"] => inside y looking at parent call frame => |foo["x"]["y"]|["z"]
+                //
+                if (node.type === WI.ScriptSyntaxTree.NodeType.ThisExpression
+                    || (characterAtPositionIsDotOrBracket && (node.type === WI.ScriptSyntaxTree.NodeType.Identifier || node.type === WI.ScriptSyntaxTree.NodeType.MemberExpression))) {
+                    let memberExpressionNode = null;
+                    for (let j = i + 1; j < nodes.length; ++j) {
+                        let nextNode = nodes[j];
+                        if (nextNode.type === WI.ScriptSyntaxTree.NodeType.MemberExpression) {
+                            memberExpressionNode = nextNode;
+                            if (position.equals(memberExpressionNode.endPosition))
+                                continue;
+                        }
+                        break;
+                    }
+
+                    if (memberExpressionNode) {
+                        highlightSourceCodeRange(memberExpressionNode.startPosition, memberExpressionNode.endPosition);
+                        return;
+                    }
+
+                    highlightSourceCodeRange(node.startPosition, node.endPosition);
+                    return;
+                }
             }
+
+            // No matches, just highlight the line.
+            callback(null);
+        });
+    }
+
+    _clearIssueWidgets()
+    {
+        for (var widget of this._widgetMap.values())
+            widget.clear();
+
+        this._widgetMap.clear();
+    }
+
+    _reinsertAllIssues()
+    {
+        this._issuesLineNumberMap.clear();
+        this._clearIssueWidgets();
+
+        let issues = WI.consoleManager.issuesForSourceCode(this._sourceCode);
+        for (let issue of issues)
+            this._addIssue(issue);
+    }
+
+    _reinsertAllThreadIndicators()
+    {
+        // Clear line styles.
+        for (let lineNumber of this._threadLineNumberMap.keys())
+            this.removeStyleClassFromLine(lineNumber, "thread-indicator");
+        this._threadLineNumberMap.clear();
+
+        // Clear widgets.
+        for (let widget of this._threadWidgetMap.values())
+            widget.clear();
+        this._threadWidgetMap.clear();
+
+        // Clear other maps.
+        this._threadTargetMap.clear();
+
+        let debuggableTargets = WI.targets;
+        if (debuggableTargets.length > 1) {
+            for (let target of debuggableTargets)
+                this._addThreadIndicatorForTarget(target);
         }
-    },
+    }
 
-    _debuggerDidPause: function(event)
+    _debuggerDidPause(event)
     {
         this._updateTokenTrackingControllerState();
         if (this._typeTokenAnnotator && this._typeTokenAnnotator.isActive())
             this._typeTokenAnnotator.refresh();
         if (this._basicBlockAnnotator && this._basicBlockAnnotator.isActive())
             this._basicBlockAnnotator.refresh();
-    },
+    }
 
-    _debuggerDidResume: function(event)
+    _debuggerDidResume(event)
     {
         this._updateTokenTrackingControllerState();
         this._dismissPopover();
@@ -1100,67 +1588,92 @@ WebInspector.SourceCodeTextEditor.prototype = {
             this._typeTokenAnnotator.refresh();
         if (this._basicBlockAnnotator && this._basicBlockAnnotator.isActive())
             this._basicBlockAnnotator.refresh();
-    },
+    }
+
+    _handleFormatterDidChange(event)
+    {
+        if (this._ignoreAllBreakpointLocationUpdates)
+            return;
+
+        // Some breakpoints / issues may have moved, some might not have. Just go through
+        // and remove and reinsert all the breakpoints / issues.
+
+        var oldBreakpointMap = this._breakpointMap;
+        this._breakpointMap = {};
+
+        for (var lineNumber in oldBreakpointMap) {
+            for (var columnNumber in oldBreakpointMap[lineNumber]) {
+                var breakpoint = oldBreakpointMap[lineNumber][columnNumber];
+                var newLineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
+                this._addBreakpointWithEditorLineInfo(breakpoint, newLineInfo);
+                this.setBreakpointInfoForLineAndColumn(lineNumber, columnNumber, null);
+                this.setBreakpointInfoForLineAndColumn(newLineInfo.lineNumber, newLineInfo.columnNumber, this._breakpointInfoForBreakpoint(breakpoint));
+            }
+        }
+
+        this._reinsertAllIssues();
+        this._reinsertAllThreadIndicators();
+    }
 
-    _sourceCodeSourceMapAdded: function(event)
+    _sourceCodeSourceMapAdded(event)
     {
-        WebInspector.notifications.addEventListener(WebInspector.Notification.GlobalModifierKeysDidChange, this._updateTokenTrackingControllerState, this);
-        this._sourceCode.removeEventListener(WebInspector.SourceCode.Event.SourceMapAdded, this._sourceCodeSourceMapAdded, this);
+        WI.notifications.addEventListener(WI.Notification.GlobalModifierKeysDidChange, this._updateTokenTrackingControllerState, this);
+        this._sourceCode.removeEventListener(WI.SourceCode.Event.SourceMapAdded, this._sourceCodeSourceMapAdded, this);
 
         this._updateTokenTrackingControllerState();
-    },
+    }
 
-    _updateTokenTrackingControllerState: function()
+    _updateTokenTrackingControllerState()
     {
-        var mode = WebInspector.CodeMirrorTokenTrackingController.Mode.None;
-        if (WebInspector.debuggerManager.paused)
-            mode = WebInspector.CodeMirrorTokenTrackingController.Mode.JavaScriptExpression;
+        var mode = WI.CodeMirrorTokenTrackingController.Mode.None;
+        if (WI.debuggerManager.paused)
+            mode = WI.CodeMirrorTokenTrackingController.Mode.JavaScriptExpression;
         else if (this._typeTokenAnnotator && this._typeTokenAnnotator.isActive())
-            mode = WebInspector.CodeMirrorTokenTrackingController.Mode.JavaScriptTypeInformation;
+            mode = WI.CodeMirrorTokenTrackingController.Mode.JavaScriptTypeInformation;
         else if (this._hasColorMarkers())
-            mode = WebInspector.CodeMirrorTokenTrackingController.Mode.MarkedTokens;
-        else if ((this._sourceCode instanceof WebInspector.SourceMapResource || this._sourceCode.sourceMaps.length !== 0) && WebInspector.modifierKeys.metaKey && !WebInspector.modifierKeys.altKey && !WebInspector.modifierKeys.shiftKey)
-            mode = WebInspector.CodeMirrorTokenTrackingController.Mode.NonSymbolTokens;
+            mode = WI.CodeMirrorTokenTrackingController.Mode.MarkedTokens;
+        else if ((this._sourceCode instanceof WI.SourceMapResource || this._sourceCode.sourceMaps.length !== 0) && WI.modifierKeys.metaKey && !WI.modifierKeys.altKey && !WI.modifierKeys.shiftKey)
+            mode = WI.CodeMirrorTokenTrackingController.Mode.NonSymbolTokens;
 
-        this.tokenTrackingController.enabled = mode !== WebInspector.CodeMirrorTokenTrackingController.Mode.None;
+        this.tokenTrackingController.enabled = mode !== WI.CodeMirrorTokenTrackingController.Mode.None;
 
         if (mode === this.tokenTrackingController.mode)
             return;
 
         switch (mode) {
-        case WebInspector.CodeMirrorTokenTrackingController.Mode.MarkedTokens:
+        case WI.CodeMirrorTokenTrackingController.Mode.MarkedTokens:
             this.tokenTrackingController.mouseOverDelayDuration = 0;
             this.tokenTrackingController.mouseOutReleaseDelayDuration = 0;
             break;
-        case WebInspector.CodeMirrorTokenTrackingController.Mode.NonSymbolTokens:
+        case WI.CodeMirrorTokenTrackingController.Mode.NonSymbolTokens:
             this.tokenTrackingController.mouseOverDelayDuration = 0;
             this.tokenTrackingController.mouseOutReleaseDelayDuration = 0;
-            this.tokenTrackingController.classNameForHighlightedRange = WebInspector.CodeMirrorTokenTrackingController.JumpToSymbolHighlightStyleClassName;
+            this.tokenTrackingController.classNameForHighlightedRange = WI.CodeMirrorTokenTrackingController.JumpToSymbolHighlightStyleClassName;
             this._dismissPopover();
             break;
-        case WebInspector.CodeMirrorTokenTrackingController.Mode.JavaScriptExpression:
-        case WebInspector.CodeMirrorTokenTrackingController.Mode.JavaScriptTypeInformation:
-            this.tokenTrackingController.mouseOverDelayDuration = WebInspector.SourceCodeTextEditor.DurationToMouseOverTokenToMakeHoveredToken;
-            this.tokenTrackingController.mouseOutReleaseDelayDuration = WebInspector.SourceCodeTextEditor.DurationToMouseOutOfHoveredTokenToRelease;
-            this.tokenTrackingController.classNameForHighlightedRange = WebInspector.SourceCodeTextEditor.HoveredExpressionHighlightStyleClassName;
+        case WI.CodeMirrorTokenTrackingController.Mode.JavaScriptExpression:
+        case WI.CodeMirrorTokenTrackingController.Mode.JavaScriptTypeInformation:
+            this.tokenTrackingController.mouseOverDelayDuration = WI.SourceCodeTextEditor.DurationToMouseOverTokenToMakeHoveredToken;
+            this.tokenTrackingController.mouseOutReleaseDelayDuration = WI.SourceCodeTextEditor.DurationToMouseOutOfHoveredTokenToRelease;
+            this.tokenTrackingController.classNameForHighlightedRange = WI.SourceCodeTextEditor.HoveredExpressionHighlightStyleClassName;
             break;
         }
 
         this.tokenTrackingController.mode = mode;
-    },
+    }
 
-    _hasColorMarkers: function()
+    _hasColorMarkers()
     {
         for (var marker of this.markers) {
-            if (marker.type === WebInspector.TextMarker.Type.Color)
+            if (marker.type === WI.TextMarker.Type.Color)
                 return true;
         }
         return false;
-    },
+    }
 
     // CodeMirrorTokenTrackingController Delegate
 
-    tokenTrackingControllerCanReleaseHighlightedRange: function(tokenTrackingController, element)
+    tokenTrackingControllerCanReleaseHighlightedRange(tokenTrackingController, element)
     {
         if (!this._popover)
             return true;
@@ -1169,65 +1682,73 @@ WebInspector.SourceCodeTextEditor.prototype = {
             return false;
 
         return true;
-    },
+    }
 
-    tokenTrackingControllerHighlightedRangeReleased: function(tokenTrackingController)
+    tokenTrackingControllerHighlightedRangeReleased(tokenTrackingController, forceHide = false)
     {
-        if (!this._mouseIsOverPopover)
+        if (forceHide || !this._mouseIsOverPopover)
             this._dismissPopover();
-    },
+    }
 
-    tokenTrackingControllerHighlightedRangeWasClicked: function(tokenTrackingController)
+    tokenTrackingControllerHighlightedRangeWasClicked(tokenTrackingController)
     {
-        if (this.tokenTrackingController.mode !== WebInspector.CodeMirrorTokenTrackingController.Mode.NonSymbolTokens)
+        if (this.tokenTrackingController.mode !== WI.CodeMirrorTokenTrackingController.Mode.NonSymbolTokens)
             return;
 
         // Links are handled by TextEditor.
         if (/\blink\b/.test(this.tokenTrackingController.candidate.hoveredToken.type))
             return;
 
+        const options = {
+            ignoreNetworkTab: true,
+            ignoreSearchTab: true,
+        };
+
         var sourceCodeLocation = this._sourceCodeLocationForEditorPosition(this.tokenTrackingController.candidate.hoveredTokenRange.start);
-        if (this.sourceCode instanceof WebInspector.SourceMapResource)
-            WebInspector.resourceSidebarPanel.showOriginalOrFormattedSourceCodeLocation(sourceCodeLocation);
+        if (this.sourceCode instanceof WI.SourceMapResource)
+            WI.showOriginalOrFormattedSourceCodeLocation(sourceCodeLocation, options);
         else
-            WebInspector.resourceSidebarPanel.showSourceCodeLocation(sourceCodeLocation);
-    },
+            WI.showSourceCodeLocation(sourceCodeLocation, options);
+    }
 
-    tokenTrackingControllerNewHighlightCandidate: function(tokenTrackingController, candidate)
+    tokenTrackingControllerNewHighlightCandidate(tokenTrackingController, candidate)
     {
-        if (this.tokenTrackingController.mode === WebInspector.CodeMirrorTokenTrackingController.Mode.NonSymbolTokens) {
+        if (this.tokenTrackingController.mode === WI.CodeMirrorTokenTrackingController.Mode.NonSymbolTokens) {
             this.tokenTrackingController.highlightRange(candidate.hoveredTokenRange);
             return;
         }
 
-        if (this.tokenTrackingController.mode === WebInspector.CodeMirrorTokenTrackingController.Mode.JavaScriptExpression) {
+        if (this.tokenTrackingController.mode === WI.CodeMirrorTokenTrackingController.Mode.JavaScriptExpression) {
             this._tokenTrackingControllerHighlightedJavaScriptExpression(candidate);
             return;
         }
 
-        if (this.tokenTrackingController.mode === WebInspector.CodeMirrorTokenTrackingController.Mode.JavaScriptTypeInformation) {
+        if (this.tokenTrackingController.mode === WI.CodeMirrorTokenTrackingController.Mode.JavaScriptTypeInformation) {
             this._tokenTrackingControllerHighlightedJavaScriptTypeInformation(candidate);
             return;
         }
 
-        if (this.tokenTrackingController.mode === WebInspector.CodeMirrorTokenTrackingController.Mode.MarkedTokens) {
+        if (this.tokenTrackingController.mode === WI.CodeMirrorTokenTrackingController.Mode.MarkedTokens) {
             var markers = this.markersAtPosition(candidate.hoveredTokenRange.start);
             if (markers.length > 0)
                 this._tokenTrackingControllerHighlightedMarkedExpression(candidate, markers);
             else
                 this._dismissEditingController();
         }
-    },
+    }
 
-    tokenTrackingControllerMouseOutOfHoveredMarker: function(tokenTrackingController, hoveredMarker)
+    tokenTrackingControllerMouseOutOfHoveredMarker(tokenTrackingController, hoveredMarker)
     {
         this._dismissEditingController();
-    },
+    }
 
-    _tokenTrackingControllerHighlightedJavaScriptExpression: function(candidate)
+    _tokenTrackingControllerHighlightedJavaScriptExpression(candidate)
     {
         console.assert(candidate.expression);
 
+        let target = WI.debuggerManager.activeCallFrame ? WI.debuggerManager.activeCallFrame.target : this.target;
+        let expression = appendWebInspectorSourceURL(candidate.expression);
+
         function populate(error, result, wasThrown)
         {
             if (error || wasThrown)
@@ -1236,52 +1757,48 @@ WebInspector.SourceCodeTextEditor.prototype = {
             if (candidate !== this.tokenTrackingController.candidate)
                 return;
 
-            var data = WebInspector.RemoteObject.fromPayload(result);
+            let data = WI.RemoteObject.fromPayload(result, target);
             switch (data.type) {
             case "function":
                 this._showPopoverForFunction(data);
                 break;
             case "object":
-                if (data.subtype === "regexp") 
-                    this._showPopoverForRegExp(data);
+                if (data.subtype === "null" || data.subtype === "regexp")
+                    this._showPopoverWithFormattedValue(data);
                 else
                     this._showPopoverForObject(data);
                 break;
             case "string":
-                this._showPopoverForString(data);
-                break;
             case "number":
-                this._showPopoverForNumber(data);
-                break;
             case "boolean":
-                this._showPopoverForBoolean(data);
-                break;
             case "undefined":
-                this._showPopoverForUndefined(data);
+            case "symbol":
+                this._showPopoverWithFormattedValue(data);
                 break;
             }
         }
 
-        if (WebInspector.debuggerManager.activeCallFrame) {
-            DebuggerAgent.evaluateOnCallFrame.invoke({callFrameId: WebInspector.debuggerManager.activeCallFrame.id, expression: candidate.expression, objectGroup: "popover", doNotPauseOnExceptionsAndMuteConsole: true}, populate.bind(this));
+
+        if (WI.debuggerManager.activeCallFrame) {
+            target.DebuggerAgent.evaluateOnCallFrame.invoke({callFrameId: WI.debuggerManager.activeCallFrame.id, expression, objectGroup: "popover", doNotPauseOnExceptionsAndMuteConsole: true}, populate.bind(this), target.DebuggerAgent);
             return;
         }
 
-        // No call frame available. Use the main page's context.
-        RuntimeAgent.evaluate.invoke({expression: candidate.expression, objectGroup: "popover", doNotPauseOnExceptionsAndMuteConsole: true}, populate.bind(this));
-    },
+        // No call frame available. Use the SourceCode's page's context.
+        target.RuntimeAgent.evaluate.invoke({expression, objectGroup: "popover", doNotPauseOnExceptionsAndMuteConsole: true}, populate.bind(this), target.RuntimeAgent);
+    }
 
-    _tokenTrackingControllerHighlightedJavaScriptTypeInformation: function(candidate)
+    _tokenTrackingControllerHighlightedJavaScriptTypeInformation(candidate)
     {
         console.assert(candidate.expression);
 
         var sourceCode = this._sourceCode;
-        var sourceID = sourceCode.scripts[0].id;
+        var sourceID = sourceCode instanceof WI.Script ? sourceCode.id : sourceCode.scripts[0].id;
         var range = candidate.hoveredTokenRange;
-        var offset = this.currentPositionToOriginalOffset({line: range.start.line, ch: range.start.ch});
+        var offset = this.currentPositionToOriginalOffset(range.start);
 
         var allRequests = [{
-            typeInformationDescriptor: WebInspector.ScriptSyntaxTree.TypeProfilerSearchDescriptor.NormalExpression,
+            typeInformationDescriptor: WI.ScriptSyntaxTree.TypeProfilerSearchDescriptor.NormalExpression,
             sourceID,
             divot: offset
         }];
@@ -1296,17 +1813,18 @@ WebInspector.SourceCodeTextEditor.prototype = {
             console.assert(allTypes.length === 1);
             if (!allTypes.length)
                 return;
-            var types = allTypes[0];
-            if (types.isValid) {
-                var popoverTitle = WebInspector.TypeTokenView.titleForPopover(WebInspector.TypeTokenView.TitleType.Variable, candidate.expression);
-                this.showPopoverForTypes(types, null, popoverTitle);
+
+            var typeDescription = WI.TypeDescription.fromPayload(allTypes[0]);
+            if (typeDescription.valid) {
+                var popoverTitle = WI.TypeTokenView.titleForPopover(WI.TypeTokenView.TitleType.Variable, candidate.expression);
+                this.showPopoverForTypes(typeDescription, null, popoverTitle);
             }
         }
 
-        RuntimeAgent.getRuntimeTypesForVariablesAtOffsets(allRequests, handler.bind(this));
-    },
+        this.target.RuntimeAgent.getRuntimeTypesForVariablesAtOffsets(allRequests, handler.bind(this));
+    }
 
-    _showPopover: function(content, bounds)
+    _showPopover(content, bounds)
     {
         console.assert(this.tokenTrackingController.candidate || bounds);
 
@@ -1319,24 +1837,30 @@ WebInspector.SourceCodeTextEditor.prototype = {
                 return;
 
             var rects = this.rectsForRange(candidate.hoveredTokenRange);
-            bounds = WebInspector.Rect.unionOfRects(rects);
+            bounds = WI.Rect.unionOfRects(rects);
+
+            if (this._popover && this._popover.visible) {
+                let intersection = bounds.intersectionWithRect(this._popover.frame);
+                if (intersection.size.width && intersection.size.height)
+                    return;
+            }
 
             shouldHighlightRange = true;
         }
 
-        content.classList.add(WebInspector.SourceCodeTextEditor.PopoverDebuggerContentStyleClassName);
+        content.classList.add(WI.SourceCodeTextEditor.PopoverDebuggerContentStyleClassName);
 
-        this._popover = this._popover || new WebInspector.Popover(this);
-        this._popover.presentNewContentWithFrame(content, bounds.pad(5), [WebInspector.RectEdge.MIN_Y, WebInspector.RectEdge.MAX_Y, WebInspector.RectEdge.MAX_X]);
+        this._popover = this._popover || new WI.Popover(this);
+        this._popover.presentNewContentWithFrame(content, bounds.pad(5), [WI.RectEdge.MIN_Y, WI.RectEdge.MAX_Y, WI.RectEdge.MAX_X]);
         if (shouldHighlightRange)
             this.tokenTrackingController.highlightRange(candidate.expressionRange);
 
         this._trackPopoverEvents();
-    },
+    }
 
-    _showPopoverForFunction: function(data)
+    _showPopoverForFunction(data)
     {
-        var candidate = this.tokenTrackingController.candidate;
+        let candidate = this.tokenTrackingController.candidate;
 
         function didGetDetails(error, response)
         {
@@ -1351,31 +1875,48 @@ WebInspector.SourceCodeTextEditor.prototype = {
             if (candidate !== this.tokenTrackingController.candidate)
                 return;
 
-            var wrapper = document.createElement("div");
-            wrapper.className = "body formatted-function";
-            wrapper.textContent = data.description;
+            let content = document.createElement("div");
+            content.classList.add("function");
 
-            var content = document.createElement("div");
-            content.className = "function";
+            let title = document.createElement("div");
+            title.classList.add("title");
+            title.textContent = response.name || response.displayName || WI.UIString("(anonymous function)");
+            content.appendChild(title);
 
-            var title = content.appendChild(document.createElement("div"));
-            title.className = "title";
-            title.textContent = response.name || response.inferredName || response.displayName || WebInspector.UIString("(anonymous function)");
+            let location = response.location;
+            let sourceCode = WI.debuggerManager.scriptForIdentifier(location.scriptId, this.target);
+            let sourceCodeLocation = sourceCode.createSourceCodeLocation(location.lineNumber, location.columnNumber);
+            let functionSourceCodeLink = WI.createSourceCodeLocationLink(sourceCodeLocation);
+            title.appendChild(functionSourceCodeLink);
 
+            let wrapper = document.createElement("div");
+            wrapper.classList.add("body");
             content.appendChild(wrapper);
 
-            this._showPopover(content);
+            let codeMirror = WI.CodeMirrorEditor.create(wrapper, {
+                mode: "text/javascript",
+                readOnly: "nocursor",
+            });
+
+            const isModule = false;
+            const indentString = WI.indentString();
+            const includeSourceMapData = false;
+            let workerProxy = WI.FormatterWorkerProxy.singleton();
+            workerProxy.formatJavaScript(data.description, isModule, indentString, includeSourceMapData, ({formattedText}) => {
+                if (candidate !== this.tokenTrackingController.candidate)
+                    return;
+
+                this._showPopover(content);
+                codeMirror.setValue(formattedText || data.description);
+                this._popover.update();
+            });
         }
-        DebuggerAgent.getFunctionDetails(data.objectId, didGetDetails.bind(this));
-    },
 
-    _showPopoverForObject: function(data)
-    {
-        if (data.subtype === "null") {
-            this._showPopoverForNull(data);
-            return;
-        }
+        data.target.DebuggerAgent.getFunctionDetails(data.objectId, didGetDetails.bind(this));
+    }
 
+    _showPopoverForObject(data)
+    {
         var content = document.createElement("div");
         content.className = "object expandable";
 
@@ -1384,76 +1925,54 @@ WebInspector.SourceCodeTextEditor.prototype = {
         titleElement.textContent = data.description;
         content.appendChild(titleElement);
 
-        var section = new WebInspector.ObjectPropertiesSection(data);
-        section.expanded = true;
-        section.element.classList.add("body");
-        content.appendChild(section.element);
-
-        this._showPopover(content);
-    },
-
-    _showPopoverForString: function(data)
-    {
-        var content = document.createElement("div");
-        content.className = "string formatted-string";
-        content.textContent = "\"" + data.description + "\"";
-
-        this._showPopover(content);
-    },
-
-    _showPopoverForRegExp: function(data)
-    {
-        var content = document.createElement("div");
-        content.className = "regexp formatted-regexp";
-        content.textContent = data.description;
-
-        this._showPopover(content);
-    },
-
-    _showPopoverForNumber: function(data)
-    {
-        var content = document.createElement("span");
-        content.className = "number formatted-number";
-        content.textContent = data.description;
+        if (data.subtype === "node") {
+            data.pushNodeToFrontend(function(nodeId) {
+                if (!nodeId)
+                    return;
 
-        this._showPopover(content);
-    },
+                var domNode = WI.domManager.nodeForId(nodeId);
+                if (!domNode.ownerDocument)
+                    return;
 
-    _showPopoverForBoolean: function(data)
-    {
-        var content = document.createElement("span");
-        content.className = "boolean formatted-boolean";
-        content.textContent = data.description;
+                var goToButton = titleElement.appendChild(WI.createGoToArrowButton());
+                goToButton.addEventListener("click", function() {
+                    WI.domManager.inspectElement(nodeId);
+                });
+            });
+        }
 
-        this._showPopover(content);
-    },
+        // FIXME: If this is a variable, it would be nice to put the variable name in the PropertyPath.
+        var objectTree = new WI.ObjectTreeView(data);
+        objectTree.showOnlyProperties();
+        objectTree.expand();
 
-    _showPopoverForNull: function(data)
-    {
-        var content = document.createElement("span");
-        content.className = "boolean formatted-null";
-        content.textContent = data.description;
+        var bodyElement = content.appendChild(document.createElement("div"));
+        bodyElement.className = "body";
+        bodyElement.appendChild(objectTree.element);
 
-        this._showPopover(content);
-    },
+        // Show the popover once we have the first set of properties for the object.
+        var candidate = this.tokenTrackingController.candidate;
+        objectTree.addEventListener(WI.ObjectTreeView.Event.Updated, function() {
+            if (candidate === this.tokenTrackingController.candidate)
+                this._showPopover(content);
+            objectTree.removeEventListener(null, null, this);
+        }, this);
+    }
 
-    _showPopoverForUndefined: function(data)
+    _showPopoverWithFormattedValue(remoteObject)
     {
-        var content = document.createElement("span");
-        content.className = "boolean formatted-undefined";
-        content.textContent = data.description;
-
+        var content = WI.FormattedValue.createElementForRemoteObject(remoteObject);
         this._showPopover(content);
-    },
+    }
 
-    willDismissPopover: function(popover)
+    willDismissPopover(popover)
     {
         this.tokenTrackingController.removeHighlightedRange();
 
-        RuntimeAgent.releaseObjectGroup("popover");
-    },
+        this.target.RuntimeAgent.releaseObjectGroup("popover");
+    }
 
-    _dismissPopover: function()
+    _dismissPopover()
     {
         if (!this._popover)
             return;
@@ -1464,44 +1983,57 @@ WebInspector.SourceCodeTextEditor.prototype = {
             this._popoverEventListenersAreRegistered = false;
             this._popoverEventListeners.unregister();
         }
-    },
+    }
 
-    _trackPopoverEvents: function()
+    _trackPopoverEvents()
     {
-        if (!this._popoverEventListeners) 
-            this._popoverEventListeners = new WebInspector.EventListenerSet(this, "Popover listeners");
+        if (!this._popoverEventListeners)
+            this._popoverEventListeners = new WI.EventListenerSet(this, "Popover listeners");
         if (!this._popoverEventListenersAreRegistered) {
             this._popoverEventListenersAreRegistered = true;
             this._popoverEventListeners.register(this._popover.element, "mouseover", this._popoverMouseover);
             this._popoverEventListeners.register(this._popover.element, "mouseout", this._popoverMouseout);
             this._popoverEventListeners.install();
         }
-    },
+    }
 
-    _popoverMouseover: function(event)
+    _popoverMouseover(event)
     {
         this._mouseIsOverPopover = true;
-    },
+    }
 
-    _popoverMouseout: function(event)
+    _popoverMouseout(event)
     {
         this._mouseIsOverPopover = this._popover.element.contains(event.relatedTarget);
-    },
+    }
+
+    _hasStyleSheetContents()
+    {
+        let mimeType = this.mimeType;
+        return mimeType === "text/css"
+            || mimeType === "text/x-less"
+            || mimeType === "text/x-sass"
+            || mimeType === "text/x-scss";
+    }
 
-    _updateEditableMarkers: function(range)
+    _updateEditableMarkers(range)
     {
-        this.createColorMarkers(range);
-        this.createGradientMarkers(range);
+        if (this._hasStyleSheetContents()) {
+            this.createColorMarkers(range);
+            this.createGradientMarkers(range);
+            this.createCubicBezierMarkers(range);
+            this.createSpringMarkers(range);
+        }
 
         this._updateTokenTrackingControllerState();
-    },
+    }
 
-    _tokenTrackingControllerHighlightedMarkedExpression: function(candidate, markers)
+    _tokenTrackingControllerHighlightedMarkedExpression(candidate, markers)
     {
         // Look for the outermost editable marker.
         var editableMarker;
         for (var marker of markers) {
-            if (!marker.range || (marker.type !== WebInspector.TextMarker.Type.Color && marker.type !== WebInspector.TextMarker.Type.Gradient))
+            if (!marker.range || !Object.values(WI.TextMarker.Type).includes(marker.type))
                 continue;
 
             if (!editableMarker || (marker.range.startLine < editableMarker.range.startLine || (marker.range.startLine === editableMarker.range.startLine && marker.range.startColumn < editableMarker.range.startColumn)))
@@ -1522,31 +2054,31 @@ WebInspector.SourceCodeTextEditor.prototype = {
 
         this._editingController = this.editingControllerForMarker(editableMarker);
 
-        if (marker.type === WebInspector.TextMarker.Type.Color) {
+        if (marker.type === WI.TextMarker.Type.Color) {
             var color = this._editingController.value;
             if (!color || !color.valid) {
                 editableMarker.clear();
-                delete this._editingController;
+                this._editingController = null;
                 return;
             }
         }
 
         this._editingController.delegate = this;
         this._editingController.presentHoverMenu();
-    },
+    }
 
-    _dismissEditingController: function(discrete)
+    _dismissEditingController(discrete)
     {
         if (this._editingController)
             this._editingController.dismissHoverMenu(discrete);
-        
+
         this.tokenTrackingController.hoveredMarker = null;
-        delete this._editingController;
-    },
+        this._editingController = null;
+    }
 
     // CodeMirrorEditingController Delegate
-    
-    editingControllerDidStartEditing: function(editingController)
+
+    editingControllerDidStartEditing(editingController)
     {
         // We can pause the token tracking controller during editing, it will be reset
         // to the expected state by calling _updateEditableMarkers() in the
@@ -1555,132 +2087,209 @@ WebInspector.SourceCodeTextEditor.prototype = {
 
         // We clear the marker since we'll reset it after editing.
         editingController.marker.clear();
-        
+
         // We ignore content changes made as a result of color editing.
         this._ignoreContentDidChange++;
-    },
-    
-    editingControllerDidFinishEditing: function(editingController)
+    }
+
+    editingControllerDidFinishEditing(editingController)
     {
         this._updateEditableMarkers(editingController.range);
 
         this._ignoreContentDidChange--;
 
-        delete this._editingController;
-    },
+        this._editingController = null;
+    }
 
-    _setTypeTokenAnnotatorEnabledState: function(shouldActivate)
+    _setTypeTokenAnnotatorEnabledState(shouldActivate)
     {
-        console.assert(this._typeTokenAnnotator);
         if (!this._typeTokenAnnotator)
             return;
 
         if (shouldActivate) {
-            RuntimeAgent.enableTypeProfiler();
+            console.assert(this.visible, "Annotators should not be enabled if the TextEditor is not visible");
 
             this._typeTokenAnnotator.reset();
-            if (this._basicBlockAnnotator) {
-                console.assert(!this._basicBlockAnnotator.isActive());
-                this._basicBlockAnnotator.reset();
-            }
 
             if (!this._typeTokenScrollHandler)
                 this._enableScrollEventsForTypeTokenAnnotator();
         } else {
-            // Because we disable type profiling when exiting the inspector, there is no need to call 
-            // RuntimeAgent.disableTypeProfiler() here.  If we were to call it here, JavaScriptCore would 
-            // compile out all the necessary type profiling information, so if a user were to quickly press then 
-            // unpress the type profiling button, we wouldn't be able to re-show type information which would 
-            // provide a confusing user experience.
-
             this._typeTokenAnnotator.clear();
-            if (this._basicBlockAnnotator)
-                this._basicBlockAnnotator.clear();
 
             if (this._typeTokenScrollHandler)
                 this._disableScrollEventsForTypeTokenAnnotator();
         }
 
-        WebInspector.showJavaScriptTypeInformationSetting.value = shouldActivate;
+        WI.settings.showJavaScriptTypeInformation.value = shouldActivate;
 
         this._updateTokenTrackingControllerState();
-    },
+    }
 
-    _getAssociatedScript: function()
+    set _basicBlockAnnotatorEnabled(shouldActivate)
     {
-        var script = null;
-        // FIXME: This needs to me modified to work with HTML files with inline script tags.
-        if (this._sourceCode instanceof WebInspector.Script)
+        if (!this._basicBlockAnnotator)
+            return;
+
+        if (shouldActivate) {
+            console.assert(this.visible, "Annotators should not be enabled if the TextEditor is not visible");
+
+            console.assert(!this._basicBlockAnnotator.isActive());
+            this._basicBlockAnnotator.reset();
+
+            if (!this._controlFlowScrollHandler)
+                this._enableScrollEventsForControlFlowAnnotator();
+        } else {
+            this._basicBlockAnnotator.clear();
+
+            if (this._controlFlowScrollHandler)
+                this._disableScrollEventsForControlFlowAnnotator();
+        }
+
+        WI.settings.enableControlFlowProfiler.value = shouldActivate;
+    }
+
+    _getAssociatedScript(position)
+    {
+        let script = null;
+
+        if (this._sourceCode instanceof WI.Script)
             script = this._sourceCode;
-        else if (this._sourceCode instanceof WebInspector.Resource && this._sourceCode.type === WebInspector.Resource.Type.Script && this._sourceCode.scripts.length)
-            script = this._sourceCode.scripts[0];
+        else if (this._sourceCode instanceof WI.Resource && this._sourceCode.scripts.length) {
+            if (this._sourceCode.type === WI.Resource.Type.Script)
+                script = this._sourceCode.scripts[0];
+            else if (this._sourceCode.type === WI.Resource.Type.Document && position) {
+                for (let inlineScript of this._sourceCode.scripts) {
+                    if (inlineScript.range.contains(position.lineNumber, position.columnNumber)) {
+                        if (isNaN(inlineScript.range.startOffset))
+                            inlineScript.range.resolveOffsets(this._sourceCode.content);
+                        script = inlineScript;
+                        break;
+                    }
+                }
+            }
+        }
+
         return script;
-    },
+    }
 
-    _makeTypeTokenAnnotator: function()
+    _createTypeTokenAnnotator()
     {
-        if (!RuntimeAgent.getRuntimeTypesForVariablesAtOffsets)
+        // COMPATIBILITY (iOS 8): Runtime.getRuntimeTypesForVariablesAtOffsets did not exist yet.
+        if (!this.target.RuntimeAgent.getRuntimeTypesForVariablesAtOffsets)
             return;
 
         var script = this._getAssociatedScript();
         if (!script)
             return;
 
-        this._typeTokenAnnotator = new WebInspector.TypeTokenAnnotator(this, script);
-    },
+        this._typeTokenAnnotator = new WI.TypeTokenAnnotator(this, script);
+    }
 
-    _makeBasicBlockAnnotator: function()
+    _createBasicBlockAnnotator()
     {
-        if (!RuntimeAgent.getBasicBlocks)
+        // COMPATIBILITY (iOS 8): Runtime.getBasicBlocks did not exist yet.
+        if (!this.target.RuntimeAgent.getBasicBlocks)
             return;
 
         var script = this._getAssociatedScript();
         if (!script)
             return;
 
-        this._basicBlockAnnotator = new WebInspector.BasicBlockAnnotator(this, script);
-    },
+        this._basicBlockAnnotator = new WI.BasicBlockAnnotator(this, script);
+    }
 
-    _enableScrollEventsForTypeTokenAnnotator: function()
+    _enableScrollEventsForTypeTokenAnnotator()
     {
         // Pause updating type tokens while scrolling to prevent frame loss.
         console.assert(!this._typeTokenScrollHandler);
-        this._typeTokenScrollHandler = this._makeTypeTokenScrollEventHandler();
+        this._typeTokenScrollHandler = this._createTypeTokenScrollEventHandler();
         this.addScrollHandler(this._typeTokenScrollHandler);
-    },
+    }
+
+    _enableScrollEventsForControlFlowAnnotator()
+    {
+        console.assert(!this._controlFlowScrollHandler);
+        this._controlFlowScrollHandler = this._createControlFlowScrollEventHandler();
+        this.addScrollHandler(this._controlFlowScrollHandler);
+    }
 
-    _disableScrollEventsForTypeTokenAnnotator: function()
+    _disableScrollEventsForTypeTokenAnnotator()
     {
         console.assert(this._typeTokenScrollHandler);
         this.removeScrollHandler(this._typeTokenScrollHandler);
         this._typeTokenScrollHandler = null;
-    },
+    }
 
-    _makeTypeTokenScrollEventHandler: function()
+    _disableScrollEventsForControlFlowAnnotator()
     {
-        var timeoutIdentifier = null;
-        function scrollHandler()
-        {
+        console.assert(this._controlFlowScrollHandler);
+        this.removeScrollHandler(this._controlFlowScrollHandler);
+        this._controlFlowScrollHandler = null;
+    }
+
+    _createTypeTokenScrollEventHandler()
+    {
+        let timeoutIdentifier = null;
+        let scrollHandler = () => {
             if (timeoutIdentifier)
                 clearTimeout(timeoutIdentifier);
             else {
                 if (this._typeTokenAnnotator)
                     this._typeTokenAnnotator.pause();
-                if (this._basicBlockAnnotator)
-                    this._basicBlockAnnotator.pause();
             }
 
-            timeoutIdentifier = setTimeout(function() {
+            timeoutIdentifier = setTimeout(() => {
                 timeoutIdentifier = null;
                 if (this._typeTokenAnnotator)
                     this._typeTokenAnnotator.resume();
+            }, WI.SourceCodeTextEditor.DurationToUpdateTypeTokensAfterScrolling);
+        };
+
+        return scrollHandler;
+    }
+
+    _createControlFlowScrollEventHandler()
+    {
+        let timeoutIdentifier = null;
+        let scrollHandler = () => {
+            if (timeoutIdentifier)
+                clearTimeout(timeoutIdentifier);
+            else if (this._basicBlockAnnotator)
+                this._basicBlockAnnotator.pause();
+
+            timeoutIdentifier = setTimeout(() => {
+                timeoutIdentifier = null;
                 if (this._basicBlockAnnotator)
                     this._basicBlockAnnotator.resume();
-            }.bind(this), WebInspector.SourceCodeTextEditor.DurationToUpdateTypeTokensAfterScrolling);
+            }, WI.SourceCodeTextEditor.DurationToUpdateTypeTokensAfterScrolling);
+        };
+
+        return scrollHandler;
+    }
+
+    _logCleared(event)
+    {
+        for (let lineNumber of this._issuesLineNumberMap.keys()) {
+            this.removeStyleClassFromLine(lineNumber, WI.SourceCodeTextEditor.LineErrorStyleClassName);
+            this.removeStyleClassFromLine(lineNumber, WI.SourceCodeTextEditor.LineWarningStyleClassName);
         }
 
-        return scrollHandler.bind(this);
+        this._issuesLineNumberMap.clear();
+        this._clearIssueWidgets();
     }
 };
 
-WebInspector.SourceCodeTextEditor.prototype.__proto__ = WebInspector.TextEditor.prototype;
+WI.SourceCodeTextEditor.LineErrorStyleClassName = "error";
+WI.SourceCodeTextEditor.LineWarningStyleClassName = "warning";
+WI.SourceCodeTextEditor.PopoverDebuggerContentStyleClassName = "debugger-popover-content";
+WI.SourceCodeTextEditor.HoveredExpressionHighlightStyleClassName = "hovered-expression-highlight";
+WI.SourceCodeTextEditor.DurationToMouseOverTokenToMakeHoveredToken = 500;
+WI.SourceCodeTextEditor.DurationToMouseOutOfHoveredTokenToRelease = 1000;
+WI.SourceCodeTextEditor.DurationToUpdateTypeTokensAfterScrolling = 100;
+WI.SourceCodeTextEditor.WidgetContainsMultipleIssuesSymbol = Symbol("source-code-widget-contains-multiple-issues");
+WI.SourceCodeTextEditor.WidgetContainsMultipleThreadsSymbol = Symbol("source-code-widget-contains-multiple-threads");
+
+WI.SourceCodeTextEditor.Event = {
+    ContentWillPopulate: "source-code-text-editor-content-will-populate",
+    ContentDidPopulate: "source-code-text-editor-content-did-populate"
+};