2 * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
26 WebInspector.SourceCodeTextEditor = class SourceCodeTextEditor extends WebInspector.TextEditor
28 constructor(sourceCode)
30 console.assert(sourceCode instanceof WebInspector.SourceCode);
36 this._sourceCode = sourceCode;
37 this._breakpointMap = {};
38 this._issuesLineNumberMap = new Map;
39 this._widgetMap = new Map;
40 this._contentPopulated = false;
41 this._invalidLineNumbers = {0: true};
42 this._ignoreContentDidChange = 0;
43 this._requestingScriptContent = false;
45 this._typeTokenScrollHandler = null;
46 this._typeTokenAnnotator = null;
47 this._basicBlockAnnotator = null;
49 this._autoFormat = false;
50 this._isProbablyMinified = false;
52 // FIXME: Currently this just jumps between resources and related source map resources. It doesn't "jump to symbol" yet.
53 this._updateTokenTrackingControllerState();
55 this.element.classList.add("source-code");
57 if (this._supportsDebugging) {
58 WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.DisabledStateDidChange, this._breakpointStatusDidChange, this);
59 WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.AutoContinueDidChange, this._breakpointStatusDidChange, this);
60 WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.ResolvedStateDidChange, this._breakpointStatusDidChange, this);
61 WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.LocationDidChange, this._updateBreakpointLocation, this);
63 WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.BreakpointsEnabledDidChange, this._breakpointsEnabledDidChange, this);
64 WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.BreakpointAdded, this._breakpointAdded, this);
65 WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.BreakpointRemoved, this._breakpointRemoved, this);
66 WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.ActiveCallFrameDidChange, this._activeCallFrameDidChange, this);
68 WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Paused, this._debuggerDidPause, this);
69 WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Resumed, this._debuggerDidResume, this);
70 if (WebInspector.debuggerManager.activeCallFrame)
71 this._debuggerDidPause();
73 this._activeCallFrameDidChange();
76 WebInspector.issueManager.addEventListener(WebInspector.IssueManager.Event.IssueWasAdded, this._issueWasAdded, this);
78 if (this._sourceCode instanceof WebInspector.SourceMapResource || this._sourceCode.sourceMaps.length > 0)
79 WebInspector.notifications.addEventListener(WebInspector.Notification.GlobalModifierKeysDidChange, this._updateTokenTrackingControllerState, this);
81 this._sourceCode.addEventListener(WebInspector.SourceCode.Event.SourceMapAdded, this._sourceCodeSourceMapAdded, this);
83 sourceCode.requestContent().then(this._contentAvailable.bind(this));
85 // FIXME: Cmd+L shortcut doesn't actually work.
86 new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Command, "L", this.showGoToLineDialog.bind(this), this.element);
87 new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Control, "G", this.showGoToLineDialog.bind(this), this.element);
89 WebInspector.logManager.addEventListener(WebInspector.LogManager.Event.Cleared, this._logCleared, this);
96 return this._sourceCode;
103 if (WebInspector.showJavaScriptTypeInformationSetting.value) {
104 if (this._typeTokenAnnotator)
105 this._typeTokenAnnotator.resume();
106 if (this._basicBlockAnnotator)
107 this._basicBlockAnnotator.resume();
108 if (!this._typeTokenScrollHandler && (this._typeTokenAnnotator || this._basicBlockAnnotator))
109 this._enableScrollEventsForTypeTokenAnnotator();
111 if (this._typeTokenAnnotator || this._basicBlockAnnotator)
112 this._setTypeTokenAnnotatorEnabledState(false);
120 this.tokenTrackingController.removeHighlightedRange();
122 this._dismissPopover();
124 this._dismissEditingController(true);
126 if (this._typeTokenAnnotator)
127 this._typeTokenAnnotator.pause();
128 if (this._basicBlockAnnotator)
129 this._basicBlockAnnotator.pause();
134 if (this._supportsDebugging) {
135 WebInspector.Breakpoint.removeEventListener(null, null, this);
136 WebInspector.debuggerManager.removeEventListener(null, null, this);
138 if (this._activeCallFrameSourceCodeLocation) {
139 this._activeCallFrameSourceCodeLocation.removeEventListener(WebInspector.SourceCodeLocation.Event.LocationChanged, this._activeCallFrameSourceCodeLocationChanged, this);
140 delete this._activeCallFrameSourceCodeLocation;
144 WebInspector.issueManager.removeEventListener(WebInspector.IssueManager.Event.IssueWasAdded, this._issueWasAdded, this);
146 if (this._sourceCode instanceof WebInspector.SourceMapResource || this._sourceCode.sourceMaps.length > 0)
147 WebInspector.notifications.removeEventListener(WebInspector.Notification.GlobalModifierKeysDidChange, this._updateTokenTrackingControllerState, this);
149 this._sourceCode.removeEventListener(WebInspector.SourceCode.Event.SourceMapAdded, this._sourceCodeSourceMapAdded, this);
154 // Currently we assume that source map resources are formatted how the author wants it.
155 // We could allow source map resources to be formatted, we would then need to make
156 // SourceCodeLocation watch listen for mappedResource's formatting changes, and keep
157 // a formatted location alongside the regular mapped location.
158 if (this._sourceCode instanceof WebInspector.SourceMapResource)
161 return super.canBeFormatted();
164 canShowTypeAnnotations()
166 return !!this._typeTokenAnnotator;
169 customPerformSearch(query)
171 function searchResultCallback(error, matches)
173 // Bail if the query changed since we started.
174 if (this.currentSearchQuery !== query)
177 if (error || !matches || !matches.length) {
178 // Report zero matches.
179 this.dispatchEventToListeners(WebInspector.TextEditor.Event.NumberOfSearchResultsDidChange);
183 var queryRegex = new RegExp(query.escapeForRegExp(), "gi");
184 var searchResults = [];
186 for (var i = 0; i < matches.length; ++i) {
187 var matchLineNumber = matches[i].lineNumber;
188 var line = this.line(matchLineNumber);
192 // Reset the last index to reuse the regex on a new line.
193 queryRegex.lastIndex = 0;
195 // Search the line and mark the ranges.
196 var lineMatch = null;
197 while (queryRegex.lastIndex + query.length <= line.length && (lineMatch = queryRegex.exec(line))) {
198 var resultTextRange = new WebInspector.TextRange(matchLineNumber, lineMatch.index, matchLineNumber, queryRegex.lastIndex);
199 searchResults.push(resultTextRange);
203 this.addSearchResults(searchResults);
205 this.dispatchEventToListeners(WebInspector.TextEditor.Event.NumberOfSearchResultsDidChange);
211 if (this._sourceCode instanceof WebInspector.SourceMapResource)
214 if (this._sourceCode instanceof WebInspector.Resource)
215 PageAgent.searchInResource(this._sourceCode.parentFrame.id, this._sourceCode.url, query, false, false, searchResultCallback.bind(this));
216 else if (this._sourceCode instanceof WebInspector.Script)
217 DebuggerAgent.searchInContent(this._sourceCode.id, query, false, false, searchResultCallback.bind(this));
223 if (!this._goToLineDialog)
224 this._goToLineDialog = new WebInspector.GoToLineDialog(this);
226 this._goToLineDialog.present(this.element);
229 isDialogRepresentedObjectValid(goToLineDialog, lineNumber)
231 return !isNaN(lineNumber) && lineNumber > 0 && lineNumber <= this.lineCount;
234 dialogWasDismissed(goToLineDialog)
236 let lineNumber = goToLineDialog.representedObject;
237 let position = new WebInspector.SourceCodePosition(lineNumber - 1, 0);
238 let range = new WebInspector.TextRange(lineNumber - 1, 0, lineNumber, 0);
240 this.revealPosition(position, range, false, true);
244 contentDidChange(replacedRanges, newRanges)
246 super.contentDidChange(replacedRanges, newRanges);
248 if (this._ignoreContentDidChange > 0)
251 for (var range of newRanges)
252 this._updateEditableMarkers(range);
254 if (this._typeTokenAnnotator || this._basicBlockAnnotator) {
255 this._setTypeTokenAnnotatorEnabledState(false);
256 this._typeTokenAnnotator = null;
257 this._basicBlockAnnotator = null;
261 toggleTypeAnnotations()
263 if (!this._typeTokenAnnotator)
266 var newActivatedState = !this._typeTokenAnnotator.isActive();
267 if (newActivatedState && this._isProbablyMinified && !this.formatted) {
268 this.updateFormattedState(true).then(() => {
269 this._setTypeTokenAnnotatorEnabledState(newActivatedState);
274 this._setTypeTokenAnnotatorEnabledState(newActivatedState);
275 return newActivatedState;
278 showPopoverForTypes(typeDescription, bounds, title)
280 var content = document.createElement("div");
281 content.className = "object expandable";
283 var titleElement = document.createElement("div");
284 titleElement.className = "title";
285 titleElement.textContent = title;
286 content.appendChild(titleElement);
288 var bodyElement = content.appendChild(document.createElement("div"));
289 bodyElement.className = "body";
291 var typeTreeView = new WebInspector.TypeTreeView(typeDescription);
292 bodyElement.appendChild(typeTreeView.element);
294 this._showPopover(content, bounds);
301 // The annotators must be cleared before pretty printing takes place and resumed
302 // after so that they clear their annotations in a known state and insert new annotations
304 var shouldResumeTypeTokenAnnotator = this._typeTokenAnnotator && this._typeTokenAnnotator.isActive();
305 var shouldResumeBasicBlockAnnotator = this._basicBlockAnnotator && this._basicBlockAnnotator.isActive();
306 if (shouldResumeTypeTokenAnnotator || shouldResumeBasicBlockAnnotator)
307 this._setTypeTokenAnnotatorEnabledState(false);
309 return super.prettyPrint(pretty).then(() => {
310 if (pretty || !this._isProbablyMinified) {
311 if (shouldResumeTypeTokenAnnotator || shouldResumeBasicBlockAnnotator)
312 this._setTypeTokenAnnotatorEnabledState(true);
314 console.assert(!pretty && this._isProbablyMinified);
315 if (this._typeTokenAnnotator || this._basicBlockAnnotator)
316 this._setTypeTokenAnnotatorEnabledState(false);
323 _unformattedLineInfoForEditorLineInfo(lineInfo)
325 if (this.formatterSourceMap)
326 return this.formatterSourceMap.formattedToOriginal(lineInfo.lineNumber, lineInfo.columnNumber);
330 _sourceCodeLocationForEditorPosition(position)
332 var lineInfo = {lineNumber: position.line, columnNumber: position.ch};
333 var unformattedLineInfo = this._unformattedLineInfoForEditorLineInfo(lineInfo);
334 return this.sourceCode.createSourceCodeLocation(unformattedLineInfo.lineNumber, unformattedLineInfo.columnNumber);
337 _editorLineInfoForSourceCodeLocation(sourceCodeLocation)
339 if (this._sourceCode instanceof WebInspector.SourceMapResource)
340 return {lineNumber: sourceCodeLocation.displayLineNumber, columnNumber: sourceCodeLocation.displayColumnNumber};
341 return {lineNumber: sourceCodeLocation.formattedLineNumber, columnNumber: sourceCodeLocation.formattedColumnNumber};
344 _breakpointForEditorLineInfo(lineInfo)
346 if (!this._breakpointMap[lineInfo.lineNumber])
348 return this._breakpointMap[lineInfo.lineNumber][lineInfo.columnNumber];
351 _addBreakpointWithEditorLineInfo(breakpoint, lineInfo)
353 if (!this._breakpointMap[lineInfo.lineNumber])
354 this._breakpointMap[lineInfo.lineNumber] = {};
356 this._breakpointMap[lineInfo.lineNumber][lineInfo.columnNumber] = breakpoint;
359 _removeBreakpointWithEditorLineInfo(breakpoint, lineInfo)
361 console.assert(breakpoint === this._breakpointMap[lineInfo.lineNumber][lineInfo.columnNumber]);
363 delete this._breakpointMap[lineInfo.lineNumber][lineInfo.columnNumber];
365 if (isEmptyObject(this._breakpointMap[lineInfo.lineNumber]))
366 delete this._breakpointMap[lineInfo.lineNumber];
369 _isLikelyMinified(content)
371 let whiteSpaceCount = 0;
374 for (let i = 0, size = Math.min(5000, content.length); i < size; i++) {
375 let char = content[i];
376 if (char === " " || char === "\n" || char === "\t")
380 ratio = whiteSpaceCount / i;
389 _populateWithContent(content)
391 content = content || "";
393 this._prepareEditorForInitialContent(content);
395 // If we can auto format, format the TextEditor before showing it.
396 if (this._autoFormat) {
397 console.assert(!this.formatted);
398 this._autoFormat = false;
399 this.deferReveal = true;
400 this.string = content;
401 this.deferReveal = false;
402 this.updateFormattedState(true).then(() => {
403 this._proceedPopulateWithContent(this.string);
408 this._proceedPopulateWithContent(content);
411 _proceedPopulateWithContent(content)
413 this.dispatchEventToListeners(WebInspector.SourceCodeTextEditor.Event.ContentWillPopulate);
415 this.string = content;
417 this._makeTypeTokenAnnotator();
418 this._makeBasicBlockAnnotator();
420 if (WebInspector.showJavaScriptTypeInformationSetting.value) {
421 if (this._basicBlockAnnotator || this._typeTokenAnnotator)
422 this._setTypeTokenAnnotatorEnabledState(true);
425 this._contentDidPopulate();
428 _contentDidPopulate()
430 this._contentPopulated = true;
432 this.dispatchEventToListeners(WebInspector.SourceCodeTextEditor.Event.ContentDidPopulate);
434 // We add the issues each time content is populated. This is needed because lines might not exist
435 // if we tried added them before when the full content wasn't available. (When populating with
436 // partial script content this can be called multiple times.)
438 this._reinsertAllIssues();
440 this._updateEditableMarkers();
443 _prepareEditorForInitialContent(content)
445 // Only do this work before the first populate.
446 if (this._contentPopulated)
449 if (this._supportsDebugging) {
450 this._breakpointMap = {};
452 var breakpoints = WebInspector.debuggerManager.breakpointsForSourceCode(this._sourceCode);
453 for (var i = 0; i < breakpoints.length; ++i) {
454 var breakpoint = breakpoints[i];
455 console.assert(this._matchesBreakpoint(breakpoint));
456 var lineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
457 this._addBreakpointWithEditorLineInfo(breakpoint, lineInfo);
458 this.setBreakpointInfoForLineAndColumn(lineInfo.lineNumber, lineInfo.columnNumber, this._breakpointInfoForBreakpoint(breakpoint));
462 if (this._sourceCode instanceof WebInspector.Resource)
463 this.mimeType = this._sourceCode.syntheticMIMEType;
464 else if (this._sourceCode instanceof WebInspector.Script)
465 this.mimeType = "text/javascript";
467 // Decide to automatically format the content if it looks minified and it can be formatted.
468 console.assert(!this.formatted);
469 if (this.canBeFormatted() && this._isLikelyMinified(content)) {
470 this._autoFormat = true;
471 this._isProbablyMinified = true;
475 _contentAvailable(parameters)
477 // Return if resource is not available.
478 if (parameters.error)
481 var sourceCode = parameters.sourceCode;
482 var content = sourceCode.content;
483 var base64Encoded = parameters.base64Encoded;
485 console.assert(sourceCode === this._sourceCode);
486 console.assert(!base64Encoded);
488 // Abort if the full content populated while waiting for this async callback.
489 if (this._fullContentPopulated)
492 this._fullContentPopulated = true;
493 this._invalidLineNumbers = {};
495 this._populateWithContent(content);
498 _breakpointStatusDidChange(event)
500 this._updateBreakpointStatus(event.target);
503 _breakpointsEnabledDidChange()
505 console.assert(this._supportsDebugging);
507 var breakpoints = WebInspector.debuggerManager.breakpointsForSourceCode(this._sourceCode);
508 for (var breakpoint of breakpoints)
509 this._updateBreakpointStatus(breakpoint);
512 _updateBreakpointStatus(breakpoint)
514 console.assert(this._supportsDebugging);
516 if (!this._contentPopulated)
519 if (!this._matchesBreakpoint(breakpoint))
522 var lineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
523 this.setBreakpointInfoForLineAndColumn(lineInfo.lineNumber, lineInfo.columnNumber, this._breakpointInfoForBreakpoint(breakpoint));
526 _updateBreakpointLocation(event)
528 console.assert(this._supportsDebugging);
530 if (!this._contentPopulated)
533 var breakpoint = event.target;
534 if (!this._matchesBreakpoint(breakpoint))
537 if (this._ignoreAllBreakpointLocationUpdates)
540 if (breakpoint === this._ignoreLocationUpdateBreakpoint)
543 var sourceCodeLocation = breakpoint.sourceCodeLocation;
545 if (this._sourceCode instanceof WebInspector.SourceMapResource) {
546 // Update our breakpoint location if the display location changed.
547 if (sourceCodeLocation.displaySourceCode !== this._sourceCode)
549 var oldLineInfo = {lineNumber: event.data.oldDisplayLineNumber, columnNumber: event.data.oldDisplayColumnNumber};
550 var newLineInfo = {lineNumber: sourceCodeLocation.displayLineNumber, columnNumber: sourceCodeLocation.displayColumnNumber};
552 // Update our breakpoint location if the original location changed.
553 if (sourceCodeLocation.sourceCode !== this._sourceCode)
555 var oldLineInfo = {lineNumber: event.data.oldFormattedLineNumber, columnNumber: event.data.oldFormattedColumnNumber};
556 var newLineInfo = {lineNumber: sourceCodeLocation.formattedLineNumber, columnNumber: sourceCodeLocation.formattedColumnNumber};
559 var existingBreakpoint = this._breakpointForEditorLineInfo(oldLineInfo);
560 if (!existingBreakpoint)
563 console.assert(breakpoint === existingBreakpoint);
565 this.setBreakpointInfoForLineAndColumn(oldLineInfo.lineNumber, oldLineInfo.columnNumber, null);
566 this.setBreakpointInfoForLineAndColumn(newLineInfo.lineNumber, newLineInfo.columnNumber, this._breakpointInfoForBreakpoint(breakpoint));
568 this._removeBreakpointWithEditorLineInfo(breakpoint, oldLineInfo);
569 this._addBreakpointWithEditorLineInfo(breakpoint, newLineInfo);
572 _breakpointAdded(event)
574 console.assert(this._supportsDebugging);
576 if (!this._contentPopulated)
579 var breakpoint = event.data.breakpoint;
580 if (!this._matchesBreakpoint(breakpoint))
583 if (breakpoint === this._ignoreBreakpointAddedBreakpoint)
586 var lineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
587 this._addBreakpointWithEditorLineInfo(breakpoint, lineInfo);
588 this.setBreakpointInfoForLineAndColumn(lineInfo.lineNumber, lineInfo.columnNumber, this._breakpointInfoForBreakpoint(breakpoint));
591 _breakpointRemoved(event)
593 console.assert(this._supportsDebugging);
595 if (!this._contentPopulated)
598 var breakpoint = event.data.breakpoint;
599 if (!this._matchesBreakpoint(breakpoint))
602 if (breakpoint === this._ignoreBreakpointRemovedBreakpoint)
605 var lineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
606 this._removeBreakpointWithEditorLineInfo(breakpoint, lineInfo);
607 this.setBreakpointInfoForLineAndColumn(lineInfo.lineNumber, lineInfo.columnNumber, null);
610 _activeCallFrameDidChange()
612 console.assert(this._supportsDebugging);
614 if (this._activeCallFrameSourceCodeLocation) {
615 this._activeCallFrameSourceCodeLocation.removeEventListener(WebInspector.SourceCodeLocation.Event.LocationChanged, this._activeCallFrameSourceCodeLocationChanged, this);
616 delete this._activeCallFrameSourceCodeLocation;
619 var activeCallFrame = WebInspector.debuggerManager.activeCallFrame;
620 if (!activeCallFrame || !this._matchesSourceCodeLocation(activeCallFrame.sourceCodeLocation)) {
621 this.executionLineNumber = NaN;
622 this.executionColumnNumber = NaN;
626 this._dismissPopover();
628 this._activeCallFrameSourceCodeLocation = activeCallFrame.sourceCodeLocation;
629 this._activeCallFrameSourceCodeLocation.addEventListener(WebInspector.SourceCodeLocation.Event.LocationChanged, this._activeCallFrameSourceCodeLocationChanged, this);
631 // Don't return early if the line number didn't change. The execution state still
632 // could have changed (e.g. continuing in a loop with a breakpoint inside).
634 var lineInfo = this._editorLineInfoForSourceCodeLocation(activeCallFrame.sourceCodeLocation);
635 this.executionLineNumber = lineInfo.lineNumber;
636 this.executionColumnNumber = lineInfo.columnNumber;
638 // If we have full content or this source code isn't a Resource we can return early.
639 // Script source code populates from the request started in the constructor.
640 if (this._fullContentPopulated || !(this._sourceCode instanceof WebInspector.Resource) || this._requestingScriptContent)
643 // Since we are paused in the debugger we need to show some content, and since the Resource
644 // content hasn't populated yet we need to populate with content from the Scripts by URL.
645 // Document resources will attempt to populate the scripts as inline (in <script> tags.)
646 // Other resources are assumed to be full scripts (JavaScript resources).
647 if (this._sourceCode.type === WebInspector.Resource.Type.Document)
648 this._populateWithInlineScriptContent();
650 this._populateWithScriptContent();
653 _activeCallFrameSourceCodeLocationChanged(event)
655 console.assert(!isNaN(this.executionLineNumber));
656 if (isNaN(this.executionLineNumber))
659 console.assert(WebInspector.debuggerManager.activeCallFrame);
660 console.assert(this._activeCallFrameSourceCodeLocation === WebInspector.debuggerManager.activeCallFrame.sourceCodeLocation);
662 var lineInfo = this._editorLineInfoForSourceCodeLocation(this._activeCallFrameSourceCodeLocation);
663 this.executionLineNumber = lineInfo.lineNumber;
664 this.executionColumnNumber = lineInfo.columnNumber;
667 _populateWithInlineScriptContent()
669 console.assert(this._sourceCode instanceof WebInspector.Resource);
670 console.assert(!this._fullContentPopulated);
671 console.assert(!this._requestingScriptContent);
673 var scripts = this._sourceCode.scripts;
674 console.assert(scripts.length);
678 var pendingRequestCount = scripts.length;
680 // If the number of scripts hasn't change since the last populate, then there is nothing to do.
681 if (this._inlineScriptContentPopulated === pendingRequestCount)
684 this._inlineScriptContentPopulated = pendingRequestCount;
686 function scriptContentAvailable(parameters)
688 // Return early if we are still waiting for content from other scripts.
689 if (--pendingRequestCount)
692 this._requestingScriptContent = false;
694 // Abort if the full content populated while waiting for these async callbacks.
695 if (this._fullContentPopulated)
698 var scriptOpenTag = "<script>";
699 var scriptCloseTag = "</script>";
703 var columnNumber = 0;
705 this._invalidLineNumbers = {};
707 for (var i = 0; i < scripts.length; ++i) {
708 // Fill the line gap with newline characters.
709 for (var newLinesCount = scripts[i].range.startLine - lineNumber; newLinesCount > 0; --newLinesCount) {
711 this._invalidLineNumbers[scripts[i].range.startLine - newLinesCount] = true;
716 // Fill the column gap with space characters.
717 for (var spacesCount = scripts[i].range.startColumn - columnNumber - scriptOpenTag.length; spacesCount > 0; --spacesCount)
720 // Add script tags and content.
721 content += scriptOpenTag;
722 content += scripts[i].content;
723 content += scriptCloseTag;
725 lineNumber = scripts[i].range.endLine;
726 columnNumber = scripts[i].range.endColumn + scriptCloseTag.length;
729 this._populateWithContent(content);
732 this._requestingScriptContent = true;
734 var boundScriptContentAvailable = scriptContentAvailable.bind(this);
735 for (var i = 0; i < scripts.length; ++i)
736 scripts[i].requestContent().then(boundScriptContentAvailable);
739 _populateWithScriptContent()
741 console.assert(this._sourceCode instanceof WebInspector.Resource);
742 console.assert(!this._fullContentPopulated);
743 console.assert(!this._requestingScriptContent);
745 // We can assume this resource only has one script that starts at line/column 0.
746 var scripts = this._sourceCode.scripts;
747 console.assert(scripts.length === 1);
751 console.assert(scripts[0].range.startLine === 0);
752 console.assert(scripts[0].range.startColumn === 0);
754 function scriptContentAvailable(parameters)
756 var content = parameters.content;
757 this._requestingScriptContent = false;
759 // Abort if the full content populated while waiting for this async callback.
760 if (this._fullContentPopulated)
763 // This is the full content.
764 this._fullContentPopulated = true;
766 this._populateWithContent(content);
769 this._requestingScriptContent = true;
771 scripts[0].requestContent().then(scriptContentAvailable.bind(this));
774 _matchesSourceCodeLocation(sourceCodeLocation)
776 if (this._sourceCode instanceof WebInspector.SourceMapResource)
777 return sourceCodeLocation.displaySourceCode === this._sourceCode;
778 if (this._sourceCode instanceof WebInspector.Resource)
779 return sourceCodeLocation.sourceCode.url === this._sourceCode.url;
780 if (this._sourceCode instanceof WebInspector.Script)
781 return sourceCodeLocation.sourceCode === this._sourceCode;
785 _matchesBreakpoint(breakpoint)
787 console.assert(this._supportsDebugging);
788 if (this._sourceCode instanceof WebInspector.SourceMapResource)
789 return breakpoint.sourceCodeLocation.displaySourceCode === this._sourceCode;
790 if (this._sourceCode instanceof WebInspector.Resource)
791 return breakpoint.url === this._sourceCode.url;
792 if (this._sourceCode instanceof WebInspector.Script)
793 return breakpoint.url === this._sourceCode.url || breakpoint.scriptIdentifier === this._sourceCode.id;
797 _issueWasAdded(event)
799 var issue = event.data.issue;
800 if (!WebInspector.IssueManager.issueMatchSourceCode(issue, this._sourceCode))
803 this._addIssue(issue);
808 var sourceCodeLocation = issue.sourceCodeLocation;
809 console.assert(sourceCodeLocation, "Expected source code location to place issue.");
810 if (!sourceCodeLocation)
813 var lineNumber = sourceCodeLocation.formattedLineNumber;
815 var lineNumberIssues = this._issuesLineNumberMap.get(lineNumber);
816 if (!lineNumberIssues) {
817 lineNumberIssues = [];
818 this._issuesLineNumberMap.set(lineNumber, lineNumberIssues);
821 // Avoid displaying duplicate issues on the same line.
822 for (var existingIssue of lineNumberIssues) {
823 if (existingIssue.sourceCodeLocation.columnNumber === sourceCodeLocation.columnNumber && existingIssue.text === issue.text)
827 lineNumberIssues.push(issue);
829 if (issue.level === WebInspector.IssueMessage.Level.Error)
830 this.addStyleClassToLine(lineNumber, WebInspector.SourceCodeTextEditor.LineErrorStyleClassName);
831 else if (issue.level === WebInspector.IssueMessage.Level.Warning)
832 this.addStyleClassToLine(lineNumber, WebInspector.SourceCodeTextEditor.LineWarningStyleClassName);
834 console.error("Unknown issue level");
836 var widget = this._issueWidgetForLine(lineNumber);
838 if (issue.level === WebInspector.IssueMessage.Level.Error)
839 widget.widgetElement.classList.add(WebInspector.SourceCodeTextEditor.LineErrorStyleClassName);
840 else if (issue.level === WebInspector.IssueMessage.Level.Warning)
841 widget.widgetElement.classList.add(WebInspector.SourceCodeTextEditor.LineWarningStyleClassName);
843 this._updateIssueWidgetForIssues(widget, lineNumberIssues);
847 _issueWidgetForLine(lineNumber)
849 var widget = this._widgetMap.get(lineNumber);
853 widget = this.createWidgetForLine(lineNumber);
857 var widgetElement = widget.widgetElement;
858 widgetElement.classList.add("issue-widget", "inline");
859 widgetElement.addEventListener("click", this._handleWidgetClick.bind(this, widget, lineNumber));
861 this._widgetMap.set(lineNumber, widget);
866 _iconClassNameForIssueLevel(level)
868 if (level === WebInspector.IssueMessage.Level.Warning)
869 return "icon-warning";
871 console.assert(level === WebInspector.IssueMessage.Level.Error);
875 _updateIssueWidgetForIssues(widget, issues)
877 var widgetElement = widget.widgetElement;
878 widgetElement.removeChildren();
880 if (widgetElement.classList.contains("inline") || issues.length === 1) {
881 var arrowElement = widgetElement.appendChild(document.createElement("span"));
882 arrowElement.className = "arrow";
884 var iconElement = widgetElement.appendChild(document.createElement("span"));
885 iconElement.className = "icon";
887 var textElement = widgetElement.appendChild(document.createElement("span"));
888 textElement.className = "text";
890 if (issues.length === 1) {
891 iconElement.classList.add(this._iconClassNameForIssueLevel(issues[0].level));
892 textElement.textContent = issues[0].text;
895 var warningsCount = 0;
896 for (var issue of issues) {
897 if (issue.level === WebInspector.IssueMessage.Level.Error)
899 else if (issue.level === WebInspector.IssueMessage.Level.Warning)
903 if (warningsCount && errorsCount) {
904 iconElement.classList.add(this._iconClassNameForIssueLevel(issue.level));
905 textElement.textContent = WebInspector.UIString("%d Errors, %d Warnings").format(errorsCount, warningsCount);
906 } else if (errorsCount) {
907 iconElement.classList.add(this._iconClassNameForIssueLevel(issue.level));
908 textElement.textContent = WebInspector.UIString("%d Errors").format(errorsCount);
909 } else if (warningsCount) {
910 iconElement.classList.add(this._iconClassNameForIssueLevel(issue.level));
911 textElement.textContent = WebInspector.UIString("%d Warnings").format(warningsCount);
914 widget[WebInspector.SourceCodeTextEditor.WidgetContainsMultipleIssuesSymbol] = true;
917 for (var issue of issues) {
918 var iconElement = widgetElement.appendChild(document.createElement("span"));
919 iconElement.className = "icon";
920 iconElement.classList.add(this._iconClassNameForIssueLevel(issue.level));
922 var textElement = widgetElement.appendChild(document.createElement("span"));
923 textElement.className = "text";
924 textElement.textContent = issue.text;
926 widgetElement.appendChild(document.createElement("br"));
933 _isWidgetToggleable(widget)
935 if (widget[WebInspector.SourceCodeTextEditor.WidgetContainsMultipleIssuesSymbol])
938 if (!widget.widgetElement.classList.contains("inline"))
941 var textElement = widget.widgetElement.lastChild;
942 if (textElement.offsetWidth !== textElement.scrollWidth)
948 _handleWidgetClick(widget, lineNumber, event)
950 if (!this._isWidgetToggleable(widget))
953 widget.widgetElement.classList.toggle("inline");
955 var lineNumberIssues = this._issuesLineNumberMap.get(lineNumber);
956 this._updateIssueWidgetForIssues(widget, lineNumberIssues);
959 _breakpointInfoForBreakpoint(breakpoint)
961 return {resolved: breakpoint.resolved, disabled: breakpoint.disabled, autoContinue: breakpoint.autoContinue};
964 get _supportsDebugging()
966 if (this._sourceCode instanceof WebInspector.Resource)
967 return this._sourceCode.type === WebInspector.Resource.Type.Document || this._sourceCode.type === WebInspector.Resource.Type.Script;
968 if (this._sourceCode instanceof WebInspector.Script)
973 // TextEditor Delegate
975 textEditorBaseURL(textEditor)
977 return this._sourceCode.url;
980 textEditorShouldHideLineNumber(textEditor, lineNumber)
982 return lineNumber in this._invalidLineNumbers;
985 textEditorGutterContextMenu(textEditor, lineNumber, columnNumber, editorBreakpoints, event)
987 if (!this._supportsDebugging)
990 event.preventDefault();
992 let addBreakpoint = () => {
993 let data = this.textEditorBreakpointAdded(this, lineNumber, columnNumber);
994 this.setBreakpointInfoForLineAndColumn(data.lineNumber, data.columnNumber, data.breakpointInfo);
997 let contextMenu = WebInspector.ContextMenu.createFromEvent(event);
999 // Paused. Add Continue to Here option only if we have a script identifier for the location.
1000 if (WebInspector.debuggerManager.paused) {
1001 let editorLineInfo = {lineNumber, columnNumber};
1002 let unformattedLineInfo = this._unformattedLineInfoForEditorLineInfo(editorLineInfo);
1003 let sourceCodeLocation = this._sourceCode.createSourceCodeLocation(unformattedLineInfo.lineNumber, unformattedLineInfo.columnNumber);
1006 if (sourceCodeLocation.sourceCode instanceof WebInspector.Script)
1007 script = sourceCodeLocation.sourceCode;
1008 else if (sourceCodeLocation.sourceCode instanceof WebInspector.Resource)
1009 script = sourceCodeLocation.sourceCode.scriptForLocation(sourceCodeLocation);
1012 contextMenu.appendItem(WebInspector.UIString("Continue to Here"), () => {
1013 WebInspector.debuggerManager.continueToLocation(script.id, sourceCodeLocation.lineNumber, sourceCodeLocation.columnNumber);
1015 contextMenu.appendSeparator();
1019 let breakpoints = [];
1020 for (let lineInfo of editorBreakpoints) {
1021 let breakpoint = this._breakpointForEditorLineInfo(lineInfo);
1022 console.assert(breakpoint);
1024 breakpoints.push(breakpoint);
1028 if (!breakpoints.length) {
1029 contextMenu.appendItem(WebInspector.UIString("Add Breakpoint"), addBreakpoint.bind(this));
1033 // Single breakpoint.
1034 if (breakpoints.length === 1) {
1035 WebInspector.breakpointPopoverController.appendContextMenuItems(contextMenu, breakpoints[0], event.target);
1037 if (!WebInspector.isShowingDebuggerTab()) {
1038 contextMenu.appendSeparator();
1039 contextMenu.appendItem(WebInspector.UIString("Reveal in Debugger Tab"), () => {
1040 WebInspector.showDebuggerTab(breakpoint);
1047 // Multiple breakpoints.
1048 let removeBreakpoints = () => {
1049 for (let breakpoint of breakpoints) {
1050 if (WebInspector.debuggerManager.isBreakpointRemovable(breakpoint))
1051 WebInspector.debuggerManager.removeBreakpoint(breakpoint);
1055 let shouldDisable = breakpoints.some((breakpoint) => !breakpoint.disabled);
1056 let toggleBreakpoints = (shouldDisable) => {
1057 for (let breakpoint of breakpoints)
1058 breakpoint.disabled = shouldDisable;
1062 contextMenu.appendItem(WebInspector.UIString("Disable Breakpoints"), toggleBreakpoints);
1064 contextMenu.appendItem(WebInspector.UIString("Enable Breakpoints"), toggleBreakpoints);
1065 contextMenu.appendItem(WebInspector.UIString("Delete Breakpoints"), removeBreakpoints);
1068 textEditorBreakpointAdded(textEditor, lineNumber, columnNumber)
1070 if (!this._supportsDebugging)
1073 var editorLineInfo = {lineNumber, columnNumber};
1074 var unformattedLineInfo = this._unformattedLineInfoForEditorLineInfo(editorLineInfo);
1075 var sourceCodeLocation = this._sourceCode.createSourceCodeLocation(unformattedLineInfo.lineNumber, unformattedLineInfo.columnNumber);
1076 var breakpoint = new WebInspector.Breakpoint(sourceCodeLocation);
1078 var lineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
1079 this._addBreakpointWithEditorLineInfo(breakpoint, lineInfo);
1081 this._ignoreBreakpointAddedBreakpoint = breakpoint;
1083 var shouldSkipEventDispatch = false;
1084 var shouldSpeculativelyResolveBreakpoint = true;
1085 WebInspector.debuggerManager.addBreakpoint(breakpoint, shouldSkipEventDispatch, shouldSpeculativelyResolveBreakpoint);
1086 delete this._ignoreBreakpointAddedBreakpoint;
1088 // Return the more accurate location and breakpoint info.
1090 breakpointInfo: this._breakpointInfoForBreakpoint(breakpoint),
1091 lineNumber: lineInfo.lineNumber,
1092 columnNumber: lineInfo.columnNumber
1096 textEditorBreakpointRemoved(textEditor, lineNumber, columnNumber)
1098 console.assert(this._supportsDebugging);
1099 if (!this._supportsDebugging)
1102 var lineInfo = {lineNumber, columnNumber};
1103 var breakpoint = this._breakpointForEditorLineInfo(lineInfo);
1104 console.assert(breakpoint);
1108 this._removeBreakpointWithEditorLineInfo(breakpoint, lineInfo);
1110 this._ignoreBreakpointRemovedBreakpoint = breakpoint;
1111 WebInspector.debuggerManager.removeBreakpoint(breakpoint);
1112 delete this._ignoreBreakpointAddedBreakpoint;
1115 textEditorBreakpointMoved(textEditor, oldLineNumber, oldColumnNumber, newLineNumber, newColumnNumber)
1117 console.assert(this._supportsDebugging);
1118 if (!this._supportsDebugging)
1121 var oldLineInfo = {lineNumber: oldLineNumber, columnNumber: oldColumnNumber};
1122 var breakpoint = this._breakpointForEditorLineInfo(oldLineInfo);
1123 console.assert(breakpoint);
1127 this._removeBreakpointWithEditorLineInfo(breakpoint, oldLineInfo);
1129 var newLineInfo = {lineNumber: newLineNumber, columnNumber: newColumnNumber};
1130 var unformattedNewLineInfo = this._unformattedLineInfoForEditorLineInfo(newLineInfo);
1131 this._ignoreLocationUpdateBreakpoint = breakpoint;
1132 breakpoint.sourceCodeLocation.update(this._sourceCode, unformattedNewLineInfo.lineNumber, unformattedNewLineInfo.columnNumber);
1133 delete this._ignoreLocationUpdateBreakpoint;
1135 var accurateNewLineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
1136 this._addBreakpointWithEditorLineInfo(breakpoint, accurateNewLineInfo);
1138 if (accurateNewLineInfo.lineNumber !== newLineInfo.lineNumber || accurateNewLineInfo.columnNumber !== newLineInfo.columnNumber)
1139 this.updateBreakpointLineAndColumn(newLineInfo.lineNumber, newLineInfo.columnNumber, accurateNewLineInfo.lineNumber, accurateNewLineInfo.columnNumber);
1142 textEditorBreakpointClicked(textEditor, lineNumber, columnNumber)
1144 console.assert(this._supportsDebugging);
1145 if (!this._supportsDebugging)
1148 var breakpoint = this._breakpointForEditorLineInfo({lineNumber, columnNumber});
1149 console.assert(breakpoint);
1153 breakpoint.cycleToNextMode();
1156 textEditorUpdatedFormatting(textEditor)
1158 this._ignoreAllBreakpointLocationUpdates = true;
1159 this._sourceCode.formatterSourceMap = this.formatterSourceMap;
1160 this._ignoreAllBreakpointLocationUpdates = false;
1162 // Always put the source map on both the Script and Resource if both exist. For example,
1163 // if this SourceCode is a Resource, then there might also be a Script. In the debugger,
1164 // the backend identifies call frames with Script line and column information, and the
1165 // Script needs the formatter source map to produce the proper display line and column.
1166 if (this._sourceCode instanceof WebInspector.Resource && !(this._sourceCode instanceof WebInspector.SourceMapResource)) {
1167 var scripts = this._sourceCode.scripts;
1168 for (var i = 0; i < scripts.length; ++i)
1169 scripts[i].formatterSourceMap = this.formatterSourceMap;
1170 } else if (this._sourceCode instanceof WebInspector.Script) {
1171 if (this._sourceCode.resource)
1172 this._sourceCode.resource.formatterSourceMap = this.formatterSourceMap;
1175 // Some breakpoints / issues may have moved, some might not have. Just go through
1176 // and remove and reinsert all the breakpoints / issues.
1178 var oldBreakpointMap = this._breakpointMap;
1179 this._breakpointMap = {};
1181 for (var lineNumber in oldBreakpointMap) {
1182 for (var columnNumber in oldBreakpointMap[lineNumber]) {
1183 var breakpoint = oldBreakpointMap[lineNumber][columnNumber];
1184 var newLineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
1185 this._addBreakpointWithEditorLineInfo(breakpoint, newLineInfo);
1186 this.setBreakpointInfoForLineAndColumn(lineNumber, columnNumber, null);
1187 this.setBreakpointInfoForLineAndColumn(newLineInfo.lineNumber, newLineInfo.columnNumber, this._breakpointInfoForBreakpoint(breakpoint));
1191 this._reinsertAllIssues();
1196 for (var widget of this._widgetMap.values())
1199 this._widgetMap.clear();
1202 _reinsertAllIssues()
1204 this._issuesLineNumberMap.clear();
1205 this._clearWidgets();
1207 var issues = WebInspector.issueManager.issuesForSourceCode(this._sourceCode);
1208 for (var issue of issues)
1209 this._addIssue(issue);
1212 _debuggerDidPause(event)
1214 this._updateTokenTrackingControllerState();
1215 if (this._typeTokenAnnotator && this._typeTokenAnnotator.isActive())
1216 this._typeTokenAnnotator.refresh();
1217 if (this._basicBlockAnnotator && this._basicBlockAnnotator.isActive())
1218 this._basicBlockAnnotator.refresh();
1221 _debuggerDidResume(event)
1223 this._updateTokenTrackingControllerState();
1224 this._dismissPopover();
1225 if (this._typeTokenAnnotator && this._typeTokenAnnotator.isActive())
1226 this._typeTokenAnnotator.refresh();
1227 if (this._basicBlockAnnotator && this._basicBlockAnnotator.isActive())
1228 this._basicBlockAnnotator.refresh();
1231 _sourceCodeSourceMapAdded(event)
1233 WebInspector.notifications.addEventListener(WebInspector.Notification.GlobalModifierKeysDidChange, this._updateTokenTrackingControllerState, this);
1234 this._sourceCode.removeEventListener(WebInspector.SourceCode.Event.SourceMapAdded, this._sourceCodeSourceMapAdded, this);
1236 this._updateTokenTrackingControllerState();
1239 _updateTokenTrackingControllerState()
1241 var mode = WebInspector.CodeMirrorTokenTrackingController.Mode.None;
1242 if (WebInspector.debuggerManager.paused)
1243 mode = WebInspector.CodeMirrorTokenTrackingController.Mode.JavaScriptExpression;
1244 else if (this._typeTokenAnnotator && this._typeTokenAnnotator.isActive())
1245 mode = WebInspector.CodeMirrorTokenTrackingController.Mode.JavaScriptTypeInformation;
1246 else if (this._hasColorMarkers())
1247 mode = WebInspector.CodeMirrorTokenTrackingController.Mode.MarkedTokens;
1248 else if ((this._sourceCode instanceof WebInspector.SourceMapResource || this._sourceCode.sourceMaps.length !== 0) && WebInspector.modifierKeys.metaKey && !WebInspector.modifierKeys.altKey && !WebInspector.modifierKeys.shiftKey)
1249 mode = WebInspector.CodeMirrorTokenTrackingController.Mode.NonSymbolTokens;
1251 this.tokenTrackingController.enabled = mode !== WebInspector.CodeMirrorTokenTrackingController.Mode.None;
1253 if (mode === this.tokenTrackingController.mode)
1257 case WebInspector.CodeMirrorTokenTrackingController.Mode.MarkedTokens:
1258 this.tokenTrackingController.mouseOverDelayDuration = 0;
1259 this.tokenTrackingController.mouseOutReleaseDelayDuration = 0;
1261 case WebInspector.CodeMirrorTokenTrackingController.Mode.NonSymbolTokens:
1262 this.tokenTrackingController.mouseOverDelayDuration = 0;
1263 this.tokenTrackingController.mouseOutReleaseDelayDuration = 0;
1264 this.tokenTrackingController.classNameForHighlightedRange = WebInspector.CodeMirrorTokenTrackingController.JumpToSymbolHighlightStyleClassName;
1265 this._dismissPopover();
1267 case WebInspector.CodeMirrorTokenTrackingController.Mode.JavaScriptExpression:
1268 case WebInspector.CodeMirrorTokenTrackingController.Mode.JavaScriptTypeInformation:
1269 this.tokenTrackingController.mouseOverDelayDuration = WebInspector.SourceCodeTextEditor.DurationToMouseOverTokenToMakeHoveredToken;
1270 this.tokenTrackingController.mouseOutReleaseDelayDuration = WebInspector.SourceCodeTextEditor.DurationToMouseOutOfHoveredTokenToRelease;
1271 this.tokenTrackingController.classNameForHighlightedRange = WebInspector.SourceCodeTextEditor.HoveredExpressionHighlightStyleClassName;
1275 this.tokenTrackingController.mode = mode;
1280 for (var marker of this.markers) {
1281 if (marker.type === WebInspector.TextMarker.Type.Color)
1287 // CodeMirrorTokenTrackingController Delegate
1289 tokenTrackingControllerCanReleaseHighlightedRange(tokenTrackingController, element)
1294 if (!window.getSelection().isCollapsed && this._popover.element.contains(window.getSelection().anchorNode))
1300 tokenTrackingControllerHighlightedRangeReleased(tokenTrackingController, forceHide = false)
1302 if (forceHide || !this._mouseIsOverPopover)
1303 this._dismissPopover();
1306 tokenTrackingControllerHighlightedRangeWasClicked(tokenTrackingController)
1308 if (this.tokenTrackingController.mode !== WebInspector.CodeMirrorTokenTrackingController.Mode.NonSymbolTokens)
1311 // Links are handled by TextEditor.
1312 if (/\blink\b/.test(this.tokenTrackingController.candidate.hoveredToken.type))
1315 var sourceCodeLocation = this._sourceCodeLocationForEditorPosition(this.tokenTrackingController.candidate.hoveredTokenRange.start);
1316 if (this.sourceCode instanceof WebInspector.SourceMapResource)
1317 WebInspector.showOriginalOrFormattedSourceCodeLocation(sourceCodeLocation);
1319 WebInspector.showSourceCodeLocation(sourceCodeLocation);
1322 tokenTrackingControllerNewHighlightCandidate(tokenTrackingController, candidate)
1324 if (this.tokenTrackingController.mode === WebInspector.CodeMirrorTokenTrackingController.Mode.NonSymbolTokens) {
1325 this.tokenTrackingController.highlightRange(candidate.hoveredTokenRange);
1329 if (this.tokenTrackingController.mode === WebInspector.CodeMirrorTokenTrackingController.Mode.JavaScriptExpression) {
1330 this._tokenTrackingControllerHighlightedJavaScriptExpression(candidate);
1334 if (this.tokenTrackingController.mode === WebInspector.CodeMirrorTokenTrackingController.Mode.JavaScriptTypeInformation) {
1335 this._tokenTrackingControllerHighlightedJavaScriptTypeInformation(candidate);
1339 if (this.tokenTrackingController.mode === WebInspector.CodeMirrorTokenTrackingController.Mode.MarkedTokens) {
1340 var markers = this.markersAtPosition(candidate.hoveredTokenRange.start);
1341 if (markers.length > 0)
1342 this._tokenTrackingControllerHighlightedMarkedExpression(candidate, markers);
1344 this._dismissEditingController();
1348 tokenTrackingControllerMouseOutOfHoveredMarker(tokenTrackingController, hoveredMarker)
1350 this._dismissEditingController();
1353 _tokenTrackingControllerHighlightedJavaScriptExpression(candidate)
1355 console.assert(candidate.expression);
1357 function populate(error, result, wasThrown)
1359 if (error || wasThrown)
1362 if (candidate !== this.tokenTrackingController.candidate)
1365 var data = WebInspector.RemoteObject.fromPayload(result);
1366 switch (data.type) {
1368 this._showPopoverForFunction(data);
1371 if (data.subtype === "null" || data.subtype === "regexp")
1372 this._showPopoverWithFormattedValue(data);
1374 this._showPopoverForObject(data);
1381 this._showPopoverWithFormattedValue(data);
1386 var expression = appendWebInspectorSourceURL(candidate.expression);
1388 if (WebInspector.debuggerManager.activeCallFrame) {
1389 DebuggerAgent.evaluateOnCallFrame.invoke({callFrameId: WebInspector.debuggerManager.activeCallFrame.id, expression, objectGroup: "popover", doNotPauseOnExceptionsAndMuteConsole: true}, populate.bind(this));
1393 // No call frame available. Use the main page's context.
1394 RuntimeAgent.evaluate.invoke({expression, objectGroup: "popover", doNotPauseOnExceptionsAndMuteConsole: true}, populate.bind(this));
1397 _tokenTrackingControllerHighlightedJavaScriptTypeInformation(candidate)
1399 console.assert(candidate.expression);
1401 var sourceCode = this._sourceCode;
1402 var sourceID = sourceCode instanceof WebInspector.Script ? sourceCode.id : sourceCode.scripts[0].id;
1403 var range = candidate.hoveredTokenRange;
1404 var offset = this.currentPositionToOriginalOffset({line: range.start.line, ch: range.start.ch});
1406 var allRequests = [{
1407 typeInformationDescriptor: WebInspector.ScriptSyntaxTree.TypeProfilerSearchDescriptor.NormalExpression,
1412 function handler(error, allTypes) {
1416 if (candidate !== this.tokenTrackingController.candidate)
1419 console.assert(allTypes.length === 1);
1420 if (!allTypes.length)
1423 var typeDescription = WebInspector.TypeDescription.fromPayload(allTypes[0]);
1424 if (typeDescription.valid) {
1425 var popoverTitle = WebInspector.TypeTokenView.titleForPopover(WebInspector.TypeTokenView.TitleType.Variable, candidate.expression);
1426 this.showPopoverForTypes(typeDescription, null, popoverTitle);
1430 RuntimeAgent.getRuntimeTypesForVariablesAtOffsets(allRequests, handler.bind(this));
1433 _showPopover(content, bounds)
1435 console.assert(this.tokenTrackingController.candidate || bounds);
1437 var shouldHighlightRange = false;
1438 var candidate = this.tokenTrackingController.candidate;
1439 // If bounds is falsey, this is a popover introduced from a hover event.
1440 // Otherwise, this is called from TypeTokenAnnotator.
1445 var rects = this.rectsForRange(candidate.hoveredTokenRange);
1446 bounds = WebInspector.Rect.unionOfRects(rects);
1448 shouldHighlightRange = true;
1451 content.classList.add(WebInspector.SourceCodeTextEditor.PopoverDebuggerContentStyleClassName);
1453 this._popover = this._popover || new WebInspector.Popover(this);
1454 this._popover.presentNewContentWithFrame(content, bounds.pad(5), [WebInspector.RectEdge.MIN_Y, WebInspector.RectEdge.MAX_Y, WebInspector.RectEdge.MAX_X]);
1455 if (shouldHighlightRange)
1456 this.tokenTrackingController.highlightRange(candidate.expressionRange);
1458 this._trackPopoverEvents();
1461 _showPopoverForFunction(data)
1463 let candidate = this.tokenTrackingController.candidate;
1465 function didGetDetails(error, response)
1468 console.error(error);
1469 this._dismissPopover();
1473 // Nothing to do if the token has changed since the time we
1474 // asked for the function details from the backend.
1475 if (candidate !== this.tokenTrackingController.candidate)
1478 let wrapper = document.createElement("div");
1479 wrapper.classList.add("body", "formatted-function");
1480 wrapper.textContent = data.description;
1482 let content = document.createElement("div");
1483 content.classList.add("function");
1485 let location = response.location;
1486 let sourceCode = WebInspector.debuggerManager.scriptForIdentifier(location.scriptId);
1487 let sourceCodeLocation = sourceCode.createSourceCodeLocation(location.lineNumber, location.columnNumber);
1488 let functionSourceCodeLink = WebInspector.createSourceCodeLocationLink(sourceCodeLocation);
1490 let title = content.appendChild(document.createElement("div"));
1491 title.classList.add("title");
1492 title.textContent = response.name || response.displayName || WebInspector.UIString("(anonymous function)");
1493 title.appendChild(functionSourceCodeLink);
1495 content.appendChild(wrapper);
1497 this._showPopover(content);
1499 DebuggerAgent.getFunctionDetails(data.objectId, didGetDetails.bind(this));
1502 _showPopoverForObject(data)
1504 var content = document.createElement("div");
1505 content.className = "object expandable";
1507 var titleElement = document.createElement("div");
1508 titleElement.className = "title";
1509 titleElement.textContent = data.description;
1510 content.appendChild(titleElement);
1512 if (data.subtype === "node") {
1513 data.pushNodeToFrontend(function(nodeId) {
1517 var domNode = WebInspector.domTreeManager.nodeForId(nodeId);
1518 if (!domNode.ownerDocument)
1521 var goToButton = titleElement.appendChild(WebInspector.createGoToArrowButton());
1522 goToButton.addEventListener("click", function() {
1523 WebInspector.domTreeManager.inspectElement(nodeId);
1528 // FIXME: If this is a variable, it would be nice to put the variable name in the PropertyPath.
1529 var objectTree = new WebInspector.ObjectTreeView(data);
1530 objectTree.showOnlyProperties();
1531 objectTree.expand();
1533 var bodyElement = content.appendChild(document.createElement("div"));
1534 bodyElement.className = "body";
1535 bodyElement.appendChild(objectTree.element);
1537 // Show the popover once we have the first set of properties for the object.
1538 var candidate = this.tokenTrackingController.candidate;
1539 objectTree.addEventListener(WebInspector.ObjectTreeView.Event.Updated, function() {
1540 if (candidate === this.tokenTrackingController.candidate)
1541 this._showPopover(content);
1542 objectTree.removeEventListener(null, null, this);
1546 _showPopoverWithFormattedValue(remoteObject)
1548 var content = WebInspector.FormattedValue.createElementForRemoteObject(remoteObject);
1549 this._showPopover(content);
1552 willDismissPopover(popover)
1554 this.tokenTrackingController.removeHighlightedRange();
1556 RuntimeAgent.releaseObjectGroup("popover");
1564 this._popover.dismiss();
1566 if (this._popoverEventListeners && this._popoverEventListenersAreRegistered) {
1567 this._popoverEventListenersAreRegistered = false;
1568 this._popoverEventListeners.unregister();
1572 _trackPopoverEvents()
1574 if (!this._popoverEventListeners)
1575 this._popoverEventListeners = new WebInspector.EventListenerSet(this, "Popover listeners");
1576 if (!this._popoverEventListenersAreRegistered) {
1577 this._popoverEventListenersAreRegistered = true;
1578 this._popoverEventListeners.register(this._popover.element, "mouseover", this._popoverMouseover);
1579 this._popoverEventListeners.register(this._popover.element, "mouseout", this._popoverMouseout);
1580 this._popoverEventListeners.install();
1584 _popoverMouseover(event)
1586 this._mouseIsOverPopover = true;
1589 _popoverMouseout(event)
1591 this._mouseIsOverPopover = this._popover.element.contains(event.relatedTarget);
1594 _hasStyleSheetContents()
1596 let mimeType = this.mimeType;
1597 return mimeType === "text/css"
1598 || mimeType === "text/x-less"
1599 || mimeType === "text/x-sass"
1600 || mimeType === "text/x-scss";
1603 _updateEditableMarkers(range)
1605 if (this._hasStyleSheetContents()) {
1606 this.createColorMarkers(range);
1607 this.createGradientMarkers(range);
1608 this.createCubicBezierMarkers(range);
1611 this._updateTokenTrackingControllerState();
1614 _tokenTrackingControllerHighlightedMarkedExpression(candidate, markers)
1616 // Look for the outermost editable marker.
1618 for (var marker of markers) {
1619 if (!marker.range || (marker.type !== WebInspector.TextMarker.Type.Color && marker.type !== WebInspector.TextMarker.Type.Gradient && marker.type !== WebInspector.TextMarker.Type.CubicBezier))
1622 if (!editableMarker || (marker.range.startLine < editableMarker.range.startLine || (marker.range.startLine === editableMarker.range.startLine && marker.range.startColumn < editableMarker.range.startColumn)))
1623 editableMarker = marker;
1626 if (!editableMarker) {
1627 this.tokenTrackingController.hoveredMarker = null;
1631 if (this.tokenTrackingController.hoveredMarker === editableMarker)
1634 this._dismissEditingController();
1636 this.tokenTrackingController.hoveredMarker = editableMarker;
1638 this._editingController = this.editingControllerForMarker(editableMarker);
1640 if (marker.type === WebInspector.TextMarker.Type.Color) {
1641 var color = this._editingController.value;
1642 if (!color || !color.valid) {
1643 editableMarker.clear();
1644 delete this._editingController;
1649 this._editingController.delegate = this;
1650 this._editingController.presentHoverMenu();
1653 _dismissEditingController(discrete)
1655 if (this._editingController)
1656 this._editingController.dismissHoverMenu(discrete);
1658 this.tokenTrackingController.hoveredMarker = null;
1659 delete this._editingController;
1662 // CodeMirrorEditingController Delegate
1664 editingControllerDidStartEditing(editingController)
1666 // We can pause the token tracking controller during editing, it will be reset
1667 // to the expected state by calling _updateEditableMarkers() in the
1668 // editingControllerDidFinishEditing delegate.
1669 this.tokenTrackingController.enabled = false;
1671 // We clear the marker since we'll reset it after editing.
1672 editingController.marker.clear();
1674 // We ignore content changes made as a result of color editing.
1675 this._ignoreContentDidChange++;
1678 editingControllerDidFinishEditing(editingController)
1680 this._updateEditableMarkers(editingController.range);
1682 this._ignoreContentDidChange--;
1684 delete this._editingController;
1687 _setTypeTokenAnnotatorEnabledState(shouldActivate)
1689 console.assert(this._typeTokenAnnotator);
1690 if (!this._typeTokenAnnotator)
1693 if (shouldActivate) {
1694 console.assert(this.visible, "Annotators should not be enabled if the TextEditor is not visible");
1696 RuntimeAgent.enableTypeProfiler();
1698 this._typeTokenAnnotator.reset();
1699 if (this._basicBlockAnnotator) {
1700 console.assert(!this._basicBlockAnnotator.isActive());
1701 this._basicBlockAnnotator.reset();
1704 if (!this._typeTokenScrollHandler)
1705 this._enableScrollEventsForTypeTokenAnnotator();
1707 // Because we disable type profiling when exiting the inspector, there is no need to call
1708 // RuntimeAgent.disableTypeProfiler() here. If we were to call it here, JavaScriptCore would
1709 // compile out all the necessary type profiling information, so if a user were to quickly press then
1710 // unpress the type profiling button, we wouldn't be able to re-show type information which would
1711 // provide a confusing user experience.
1713 this._typeTokenAnnotator.clear();
1714 if (this._basicBlockAnnotator)
1715 this._basicBlockAnnotator.clear();
1717 if (this._typeTokenScrollHandler)
1718 this._disableScrollEventsForTypeTokenAnnotator();
1721 WebInspector.showJavaScriptTypeInformationSetting.value = shouldActivate;
1723 this._updateTokenTrackingControllerState();
1726 _getAssociatedScript()
1729 // FIXME: This needs to me modified to work with HTML files with inline script tags.
1730 if (this._sourceCode instanceof WebInspector.Script)
1731 script = this._sourceCode;
1732 else if (this._sourceCode instanceof WebInspector.Resource && this._sourceCode.type === WebInspector.Resource.Type.Script && this._sourceCode.scripts.length)
1733 script = this._sourceCode.scripts[0];
1737 _makeTypeTokenAnnotator()
1739 // COMPATIBILITY (iOS 8): Runtime.getRuntimeTypesForVariablesAtOffsets did not exist yet.
1740 if (!RuntimeAgent.getRuntimeTypesForVariablesAtOffsets)
1743 var script = this._getAssociatedScript();
1747 this._typeTokenAnnotator = new WebInspector.TypeTokenAnnotator(this, script);
1750 _makeBasicBlockAnnotator()
1752 // COMPATIBILITY (iOS 8): Runtime.getBasicBlocks did not exist yet.
1753 if (!RuntimeAgent.getBasicBlocks)
1756 var script = this._getAssociatedScript();
1760 this._basicBlockAnnotator = new WebInspector.BasicBlockAnnotator(this, script);
1763 _enableScrollEventsForTypeTokenAnnotator()
1765 // Pause updating type tokens while scrolling to prevent frame loss.
1766 console.assert(!this._typeTokenScrollHandler);
1767 this._typeTokenScrollHandler = this._makeTypeTokenScrollEventHandler();
1768 this.addScrollHandler(this._typeTokenScrollHandler);
1771 _disableScrollEventsForTypeTokenAnnotator()
1773 console.assert(this._typeTokenScrollHandler);
1774 this.removeScrollHandler(this._typeTokenScrollHandler);
1775 this._typeTokenScrollHandler = null;
1778 _makeTypeTokenScrollEventHandler()
1780 var timeoutIdentifier = null;
1781 function scrollHandler()
1783 if (timeoutIdentifier)
1784 clearTimeout(timeoutIdentifier);
1786 if (this._typeTokenAnnotator)
1787 this._typeTokenAnnotator.pause();
1788 if (this._basicBlockAnnotator)
1789 this._basicBlockAnnotator.pause();
1792 timeoutIdentifier = setTimeout(function() {
1793 timeoutIdentifier = null;
1794 if (this._typeTokenAnnotator)
1795 this._typeTokenAnnotator.resume();
1796 if (this._basicBlockAnnotator)
1797 this._basicBlockAnnotator.resume();
1798 }.bind(this), WebInspector.SourceCodeTextEditor.DurationToUpdateTypeTokensAfterScrolling);
1801 return scrollHandler.bind(this);
1806 for (let lineNumber of this._issuesLineNumberMap.keys()) {
1807 this.removeStyleClassFromLine(lineNumber, WebInspector.SourceCodeTextEditor.LineErrorStyleClassName);
1808 this.removeStyleClassFromLine(lineNumber, WebInspector.SourceCodeTextEditor.LineWarningStyleClassName);
1811 this._issuesLineNumberMap.clear();
1812 this._clearWidgets();
1816 WebInspector.SourceCodeTextEditor.LineErrorStyleClassName = "error";
1817 WebInspector.SourceCodeTextEditor.LineWarningStyleClassName = "warning";
1818 WebInspector.SourceCodeTextEditor.PopoverDebuggerContentStyleClassName = "debugger-popover-content";
1819 WebInspector.SourceCodeTextEditor.HoveredExpressionHighlightStyleClassName = "hovered-expression-highlight";
1820 WebInspector.SourceCodeTextEditor.DurationToMouseOverTokenToMakeHoveredToken = 500;
1821 WebInspector.SourceCodeTextEditor.DurationToMouseOutOfHoveredTokenToRelease = 1000;
1822 WebInspector.SourceCodeTextEditor.DurationToUpdateTypeTokensAfterScrolling = 100;
1823 WebInspector.SourceCodeTextEditor.WidgetContainsMultipleIssuesSymbol = Symbol("source-code-widget-contains-multiple-issues");
1825 WebInspector.SourceCodeTextEditor.Event = {
1826 ContentWillPopulate: "source-code-text-editor-content-will-populate",
1827 ContentDidPopulate: "source-code-text-editor-content-did-populate"