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;
44 this._typeTokenScrollHandler = null;
45 this._typeTokenAnnotator = null;
46 this._basicBlockAnnotator = null;
48 this._isProbablyMinified = false;
50 // FIXME: Currently this just jumps between resources and related source map resources. It doesn't "jump to symbol" yet.
51 this._updateTokenTrackingControllerState();
53 this.element.classList.add("source-code");
55 if (this._supportsDebugging) {
56 WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.DisabledStateDidChange, this._breakpointStatusDidChange, this);
57 WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.AutoContinueDidChange, this._breakpointStatusDidChange, this);
58 WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.ResolvedStateDidChange, this._breakpointStatusDidChange, this);
59 WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.LocationDidChange, this._updateBreakpointLocation, this);
61 WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.BreakpointsEnabledDidChange, this._breakpointsEnabledDidChange, this);
62 WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.BreakpointAdded, this._breakpointAdded, this);
63 WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.BreakpointRemoved, this._breakpointRemoved, this);
64 WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.ActiveCallFrameDidChange, this._activeCallFrameDidChange, this);
66 WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Paused, this._debuggerDidPause, this);
67 WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Resumed, this._debuggerDidResume, this);
68 if (WebInspector.debuggerManager.activeCallFrame)
69 this._debuggerDidPause();
71 this._activeCallFrameDidChange();
74 WebInspector.issueManager.addEventListener(WebInspector.IssueManager.Event.IssueWasAdded, this._issueWasAdded, this);
76 if (this._sourceCode instanceof WebInspector.SourceMapResource || this._sourceCode.sourceMaps.length > 0)
77 WebInspector.notifications.addEventListener(WebInspector.Notification.GlobalModifierKeysDidChange, this._updateTokenTrackingControllerState, this);
79 this._sourceCode.addEventListener(WebInspector.SourceCode.Event.SourceMapAdded, this._sourceCodeSourceMapAdded, this);
81 sourceCode.requestContent().then(this._contentAvailable.bind(this));
83 // FIXME: Cmd+L shorcut doesn't actually work.
84 new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Command, "L", this.showGoToLineDialog.bind(this), this.element);
85 new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Control, "G", this.showGoToLineDialog.bind(this), this.element);
92 return this._sourceCode;
99 if (WebInspector.showJavaScriptTypeInformationSetting.value) {
100 if (this._typeTokenAnnotator)
101 this._typeTokenAnnotator.resume();
102 if (this._basicBlockAnnotator)
103 this._basicBlockAnnotator.resume();
104 if (!this._typeTokenScrollHandler && (this._typeTokenAnnotator || this._basicBlockAnnotator))
105 this._enableScrollEventsForTypeTokenAnnotator();
107 if (this._typeTokenAnnotator || this._basicBlockAnnotator)
108 this._setTypeTokenAnnotatorEnabledState(false);
116 this.tokenTrackingController.removeHighlightedRange();
118 this._dismissPopover();
120 this._dismissEditingController(true);
122 if (this._typeTokenAnnotator)
123 this._typeTokenAnnotator.pause();
124 if (this._basicBlockAnnotator)
125 this._basicBlockAnnotator.pause();
130 if (this._supportsDebugging) {
131 WebInspector.Breakpoint.removeEventListener(null, null, this);
132 WebInspector.debuggerManager.removeEventListener(null, null, this);
134 if (this._activeCallFrameSourceCodeLocation) {
135 this._activeCallFrameSourceCodeLocation.removeEventListener(WebInspector.SourceCodeLocation.Event.LocationChanged, this._activeCallFrameSourceCodeLocationChanged, this);
136 delete this._activeCallFrameSourceCodeLocation;
140 WebInspector.issueManager.removeEventListener(WebInspector.IssueManager.Event.IssueWasAdded, this._issueWasAdded, this);
142 WebInspector.notifications.removeEventListener(WebInspector.Notification.GlobalModifierKeysDidChange, this._updateTokenTrackingControllerState, this);
143 this._sourceCode.removeEventListener(WebInspector.SourceCode.Event.SourceMapAdded, this._sourceCodeSourceMapAdded, this);
148 // Currently we assume that source map resources are formatted how the author wants it.
149 // We could allow source map resources to be formatted, we would then need to make
150 // SourceCodeLocation watch listen for mappedResource's formatting changes, and keep
151 // a formatted location alongside the regular mapped location.
152 if (this._sourceCode instanceof WebInspector.SourceMapResource)
155 return super.canBeFormatted();
158 canShowTypeAnnotations()
160 return !!this._typeTokenAnnotator;
163 customPerformSearch(query)
165 function searchResultCallback(error, matches)
167 // Bail if the query changed since we started.
168 if (this.currentSearchQuery !== query)
171 if (error || !matches || !matches.length) {
172 // Report zero matches.
173 this.dispatchEventToListeners(WebInspector.TextEditor.Event.NumberOfSearchResultsDidChange);
177 var queryRegex = new RegExp(query.escapeForRegExp(), "gi");
178 var searchResults = [];
180 for (var i = 0; i < matches.length; ++i) {
181 var matchLineNumber = matches[i].lineNumber;
182 var line = this.line(matchLineNumber);
186 // Reset the last index to reuse the regex on a new line.
187 queryRegex.lastIndex = 0;
189 // Search the line and mark the ranges.
190 var lineMatch = null;
191 while (queryRegex.lastIndex + query.length <= line.length && (lineMatch = queryRegex.exec(line))) {
192 var resultTextRange = new WebInspector.TextRange(matchLineNumber, lineMatch.index, matchLineNumber, queryRegex.lastIndex);
193 searchResults.push(resultTextRange);
197 this.addSearchResults(searchResults);
199 this.dispatchEventToListeners(WebInspector.TextEditor.Event.NumberOfSearchResultsDidChange);
205 if (this._sourceCode instanceof WebInspector.SourceMapResource)
208 if (this._sourceCode instanceof WebInspector.Resource)
209 PageAgent.searchInResource(this._sourceCode.parentFrame.id, this._sourceCode.url, query, false, false, searchResultCallback.bind(this));
210 else if (this._sourceCode instanceof WebInspector.Script)
211 DebuggerAgent.searchInContent(this._sourceCode.id, query, false, false, searchResultCallback.bind(this));
217 if (!this._goToLineDialog) {
218 this._goToLineDialog = new WebInspector.GoToLineDialog;
219 this._goToLineDialog.delegate = this;
222 this._goToLineDialog.present(this.element);
225 isGoToLineDialogValueValid(goToLineDialog, lineNumber)
227 return !isNaN(lineNumber) && lineNumber > 0 && lineNumber <= this.lineCount;
230 goToLineDialogValueWasValidated(goToLineDialog, lineNumber)
232 var position = new WebInspector.SourceCodePosition(lineNumber - 1, 0);
233 var range = new WebInspector.TextRange(lineNumber - 1, 0, lineNumber, 0);
234 this.revealPosition(position, range, false, true);
237 goToLineDialogWasDismissed()
242 contentDidChange(replacedRanges, newRanges)
244 super.contentDidChange(replacedRanges, newRanges);
246 if (this._ignoreContentDidChange > 0)
249 for (var range of newRanges)
250 this._updateEditableMarkers(range);
252 if (this._typeTokenAnnotator || this._basicBlockAnnotator) {
253 this._setTypeTokenAnnotatorEnabledState(false);
254 this._typeTokenAnnotator = null;
255 this._basicBlockAnnotator = null;
259 toggleTypeAnnotations()
261 if (!this._typeTokenAnnotator)
264 var newActivatedState = !this._typeTokenAnnotator.isActive();
265 if (newActivatedState && this._isProbablyMinified && !this.formatted)
266 this.formatted = true;
268 this._setTypeTokenAnnotatorEnabledState(newActivatedState);
269 return newActivatedState;
272 showPopoverForTypes(typeDescription, bounds, title)
274 var content = document.createElement("div");
275 content.className = "object expandable";
277 var titleElement = document.createElement("div");
278 titleElement.className = "title";
279 titleElement.textContent = title;
280 content.appendChild(titleElement);
282 var bodyElement = content.appendChild(document.createElement("div"));
283 bodyElement.className = "body";
285 var typeTreeView = new WebInspector.TypeTreeView(typeDescription);
286 bodyElement.appendChild(typeTreeView.element);
288 this._showPopover(content, bounds);
295 // The annotators must be cleared before pretty printing takes place and resumed
296 // after so that they clear their annotations in a known state and insert new annotations
298 var shouldResumeTypeTokenAnnotator = this._typeTokenAnnotator && this._typeTokenAnnotator.isActive();
299 var shouldResumeBasicBlockAnnotator = this._basicBlockAnnotator && this._basicBlockAnnotator.isActive();
300 if (shouldResumeTypeTokenAnnotator || shouldResumeBasicBlockAnnotator)
301 this._setTypeTokenAnnotatorEnabledState(false);
303 super.prettyPrint(pretty);
305 if (pretty || !this._isProbablyMinified) {
306 if (shouldResumeTypeTokenAnnotator || shouldResumeBasicBlockAnnotator)
307 this._setTypeTokenAnnotatorEnabledState(true);
309 console.assert(!pretty && this._isProbablyMinified);
310 if (this._typeTokenAnnotator || this._basicBlockAnnotator)
311 this._setTypeTokenAnnotatorEnabledState(false);
317 _unformattedLineInfoForEditorLineInfo(lineInfo)
319 if (this.formatterSourceMap)
320 return this.formatterSourceMap.formattedToOriginal(lineInfo.lineNumber, lineInfo.columnNumber);
324 _sourceCodeLocationForEditorPosition(position)
326 var lineInfo = {lineNumber: position.line, columnNumber: position.ch};
327 var unformattedLineInfo = this._unformattedLineInfoForEditorLineInfo(lineInfo);
328 return this.sourceCode.createSourceCodeLocation(unformattedLineInfo.lineNumber, unformattedLineInfo.columnNumber);
331 _editorLineInfoForSourceCodeLocation(sourceCodeLocation)
333 if (this._sourceCode instanceof WebInspector.SourceMapResource)
334 return {lineNumber: sourceCodeLocation.displayLineNumber, columnNumber: sourceCodeLocation.displayColumnNumber};
335 return {lineNumber: sourceCodeLocation.formattedLineNumber, columnNumber: sourceCodeLocation.formattedColumnNumber};
338 _breakpointForEditorLineInfo(lineInfo)
340 if (!this._breakpointMap[lineInfo.lineNumber])
342 return this._breakpointMap[lineInfo.lineNumber][lineInfo.columnNumber];
345 _addBreakpointWithEditorLineInfo(breakpoint, lineInfo)
347 if (!this._breakpointMap[lineInfo.lineNumber])
348 this._breakpointMap[lineInfo.lineNumber] = {};
350 this._breakpointMap[lineInfo.lineNumber][lineInfo.columnNumber] = breakpoint;
353 _removeBreakpointWithEditorLineInfo(breakpoint, lineInfo)
355 console.assert(breakpoint === this._breakpointMap[lineInfo.lineNumber][lineInfo.columnNumber]);
357 delete this._breakpointMap[lineInfo.lineNumber][lineInfo.columnNumber];
359 if (isEmptyObject(this._breakpointMap[lineInfo.lineNumber]))
360 delete this._breakpointMap[lineInfo.lineNumber];
363 _contentWillPopulate(content)
365 this.dispatchEventToListeners(WebInspector.SourceCodeTextEditor.Event.ContentWillPopulate);
367 // We only do the rest of this work before the first populate.
368 if (this._contentPopulated)
371 if (this._supportsDebugging) {
372 this._breakpointMap = {};
374 var breakpoints = WebInspector.debuggerManager.breakpointsForSourceCode(this._sourceCode);
375 for (var i = 0; i < breakpoints.length; ++i) {
376 var breakpoint = breakpoints[i];
377 console.assert(this._matchesBreakpoint(breakpoint));
378 var lineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
379 this._addBreakpointWithEditorLineInfo(breakpoint, lineInfo);
380 this.setBreakpointInfoForLineAndColumn(lineInfo.lineNumber, lineInfo.columnNumber, this._breakpointInfoForBreakpoint(breakpoint));
384 if (this._sourceCode instanceof WebInspector.Resource)
385 this.mimeType = this._sourceCode.syntheticMIMEType;
386 else if (this._sourceCode instanceof WebInspector.Script)
387 this.mimeType = "text/javascript";
389 // Automatically format the content if it looks minified and it can be formatted.
390 console.assert(!this.formatted);
391 if (this.canBeFormatted() && this._isLikelyMinified(content)) {
392 this.autoFormat = true;
393 this._isProbablyMinified = true;
397 _isLikelyMinified(content)
399 let whiteSpaceCount = 0;
402 for (let i = 0, size = Math.min(5000, content.length); i < size; i++) {
403 let char = content[i];
404 if (char === " " || char === "\n" || char === "\t")
408 ratio = whiteSpaceCount / i;
417 _contentDidPopulate()
419 this._contentPopulated = true;
421 this.dispatchEventToListeners(WebInspector.SourceCodeTextEditor.Event.ContentDidPopulate);
423 // We add the issues each time content is populated. This is needed because lines might not exist
424 // if we tried added them before when the full content wasn't available. (When populating with
425 // partial script content this can be called multiple times.)
427 this._reinsertAllIssues();
429 this._updateEditableMarkers();
432 _populateWithContent(content)
434 content = content || "";
436 this._contentWillPopulate(content);
437 this.string = content;
439 this._makeTypeTokenAnnotator();
440 this._makeBasicBlockAnnotator();
442 if (WebInspector.showJavaScriptTypeInformationSetting.value) {
443 if (this._basicBlockAnnotator || this._typeTokenAnnotator)
444 this._setTypeTokenAnnotatorEnabledState(true);
447 this._contentDidPopulate();
450 _contentAvailable(parameters)
452 // Return if resource is not available.
453 if (parameters.error)
456 var sourceCode = parameters.sourceCode;
457 var content = sourceCode.content;
458 var base64Encoded = parameters.base64Encoded;
460 console.assert(sourceCode === this._sourceCode);
461 console.assert(!base64Encoded);
463 // Abort if the full content populated while waiting for this async callback.
464 if (this._fullContentPopulated)
467 this._fullContentPopulated = true;
468 this._invalidLineNumbers = {};
470 this._populateWithContent(content);
473 _breakpointStatusDidChange(event)
475 this._updateBreakpointStatus(event.target);
478 _breakpointsEnabledDidChange()
480 console.assert(this._supportsDebugging);
482 var breakpoints = WebInspector.debuggerManager.breakpointsForSourceCode(this._sourceCode);
483 for (var breakpoint of breakpoints)
484 this._updateBreakpointStatus(breakpoint);
487 _updateBreakpointStatus(breakpoint)
489 console.assert(this._supportsDebugging);
491 if (!this._contentPopulated)
494 if (!this._matchesBreakpoint(breakpoint))
497 var lineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
498 this.setBreakpointInfoForLineAndColumn(lineInfo.lineNumber, lineInfo.columnNumber, this._breakpointInfoForBreakpoint(breakpoint));
501 _updateBreakpointLocation(event)
503 console.assert(this._supportsDebugging);
505 if (!this._contentPopulated)
508 var breakpoint = event.target;
509 if (!this._matchesBreakpoint(breakpoint))
512 if (this._ignoreAllBreakpointLocationUpdates)
515 if (breakpoint === this._ignoreLocationUpdateBreakpoint)
518 var sourceCodeLocation = breakpoint.sourceCodeLocation;
520 if (this._sourceCode instanceof WebInspector.SourceMapResource) {
521 // Update our breakpoint location if the display location changed.
522 if (sourceCodeLocation.displaySourceCode !== this._sourceCode)
524 var oldLineInfo = {lineNumber: event.data.oldDisplayLineNumber, columnNumber: event.data.oldDisplayColumnNumber};
525 var newLineInfo = {lineNumber: sourceCodeLocation.displayLineNumber, columnNumber: sourceCodeLocation.displayColumnNumber};
527 // Update our breakpoint location if the original location changed.
528 if (sourceCodeLocation.sourceCode !== this._sourceCode)
530 var oldLineInfo = {lineNumber: event.data.oldFormattedLineNumber, columnNumber: event.data.oldFormattedColumnNumber};
531 var newLineInfo = {lineNumber: sourceCodeLocation.formattedLineNumber, columnNumber: sourceCodeLocation.formattedColumnNumber};
534 var existingBreakpoint = this._breakpointForEditorLineInfo(oldLineInfo);
535 if (!existingBreakpoint)
538 console.assert(breakpoint === existingBreakpoint);
540 this.setBreakpointInfoForLineAndColumn(oldLineInfo.lineNumber, oldLineInfo.columnNumber, null);
541 this.setBreakpointInfoForLineAndColumn(newLineInfo.lineNumber, newLineInfo.columnNumber, this._breakpointInfoForBreakpoint(breakpoint));
543 this._removeBreakpointWithEditorLineInfo(breakpoint, oldLineInfo);
544 this._addBreakpointWithEditorLineInfo(breakpoint, newLineInfo);
547 _breakpointAdded(event)
549 console.assert(this._supportsDebugging);
551 if (!this._contentPopulated)
554 var breakpoint = event.data.breakpoint;
555 if (!this._matchesBreakpoint(breakpoint))
558 if (breakpoint === this._ignoreBreakpointAddedBreakpoint)
561 var lineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
562 this._addBreakpointWithEditorLineInfo(breakpoint, lineInfo);
563 this.setBreakpointInfoForLineAndColumn(lineInfo.lineNumber, lineInfo.columnNumber, this._breakpointInfoForBreakpoint(breakpoint));
566 _breakpointRemoved(event)
568 console.assert(this._supportsDebugging);
570 if (!this._contentPopulated)
573 var breakpoint = event.data.breakpoint;
574 if (!this._matchesBreakpoint(breakpoint))
577 if (breakpoint === this._ignoreBreakpointRemovedBreakpoint)
580 var lineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
581 this._removeBreakpointWithEditorLineInfo(breakpoint, lineInfo);
582 this.setBreakpointInfoForLineAndColumn(lineInfo.lineNumber, lineInfo.columnNumber, null);
585 _activeCallFrameDidChange()
587 console.assert(this._supportsDebugging);
589 if (this._activeCallFrameSourceCodeLocation) {
590 this._activeCallFrameSourceCodeLocation.removeEventListener(WebInspector.SourceCodeLocation.Event.LocationChanged, this._activeCallFrameSourceCodeLocationChanged, this);
591 delete this._activeCallFrameSourceCodeLocation;
594 var activeCallFrame = WebInspector.debuggerManager.activeCallFrame;
595 if (!activeCallFrame || !this._matchesSourceCodeLocation(activeCallFrame.sourceCodeLocation)) {
596 this.executionLineNumber = NaN;
597 this.executionColumnNumber = NaN;
601 this._dismissPopover();
603 this._activeCallFrameSourceCodeLocation = activeCallFrame.sourceCodeLocation;
604 this._activeCallFrameSourceCodeLocation.addEventListener(WebInspector.SourceCodeLocation.Event.LocationChanged, this._activeCallFrameSourceCodeLocationChanged, this);
606 // Don't return early if the line number didn't change. The execution state still
607 // could have changed (e.g. continuing in a loop with a breakpoint inside).
609 var lineInfo = this._editorLineInfoForSourceCodeLocation(activeCallFrame.sourceCodeLocation);
610 this.executionLineNumber = lineInfo.lineNumber;
611 this.executionColumnNumber = lineInfo.columnNumber;
613 // If we have full content or this source code isn't a Resource we can return early.
614 // Script source code populates from the request started in the constructor.
615 if (this._fullContentPopulated || !(this._sourceCode instanceof WebInspector.Resource) || this._requestingScriptContent)
618 // Since we are paused in the debugger we need to show some content, and since the Resource
619 // content hasn't populated yet we need to populate with content from the Scripts by URL.
620 // Document resources will attempt to populate the scripts as inline (in <script> tags.)
621 // Other resources are assumed to be full scripts (JavaScript resources).
622 if (this._sourceCode.type === WebInspector.Resource.Type.Document)
623 this._populateWithInlineScriptContent();
625 this._populateWithScriptContent();
628 _activeCallFrameSourceCodeLocationChanged(event)
630 console.assert(!isNaN(this.executionLineNumber));
631 if (isNaN(this.executionLineNumber))
634 console.assert(WebInspector.debuggerManager.activeCallFrame);
635 console.assert(this._activeCallFrameSourceCodeLocation === WebInspector.debuggerManager.activeCallFrame.sourceCodeLocation);
637 var lineInfo = this._editorLineInfoForSourceCodeLocation(this._activeCallFrameSourceCodeLocation);
638 this.executionLineNumber = lineInfo.lineNumber;
639 this.executionColumnNumber = lineInfo.columnNumber;
642 _populateWithInlineScriptContent()
644 console.assert(this._sourceCode instanceof WebInspector.Resource);
645 console.assert(!this._fullContentPopulated);
646 console.assert(!this._requestingScriptContent);
648 var scripts = this._sourceCode.scripts;
649 console.assert(scripts.length);
653 var pendingRequestCount = scripts.length;
655 // If the number of scripts hasn't change since the last populate, then there is nothing to do.
656 if (this._inlineScriptContentPopulated === pendingRequestCount)
659 this._inlineScriptContentPopulated = pendingRequestCount;
661 function scriptContentAvailable(parameters)
663 // Return early if we are still waiting for content from other scripts.
664 if (--pendingRequestCount)
667 delete this._requestingScriptContent;
669 // Abort if the full content populated while waiting for these async callbacks.
670 if (this._fullContentPopulated)
673 var scriptOpenTag = "<script>";
674 var scriptCloseTag = "</script>";
678 var columnNumber = 0;
680 this._invalidLineNumbers = {};
682 for (var i = 0; i < scripts.length; ++i) {
683 // Fill the line gap with newline characters.
684 for (var newLinesCount = scripts[i].range.startLine - lineNumber; newLinesCount > 0; --newLinesCount) {
686 this._invalidLineNumbers[scripts[i].range.startLine - newLinesCount] = true;
691 // Fill the column gap with space characters.
692 for (var spacesCount = scripts[i].range.startColumn - columnNumber - scriptOpenTag.length; spacesCount > 0; --spacesCount)
695 // Add script tags and content.
696 content += scriptOpenTag;
697 content += scripts[i].content;
698 content += scriptCloseTag;
700 lineNumber = scripts[i].range.endLine;
701 columnNumber = scripts[i].range.endColumn + scriptCloseTag.length;
704 this._populateWithContent(content);
707 this._requestingScriptContent = true;
709 var boundScriptContentAvailable = scriptContentAvailable.bind(this);
710 for (var i = 0; i < scripts.length; ++i)
711 scripts[i].requestContent().then(boundScriptContentAvailable);
714 _populateWithScriptContent()
716 console.assert(this._sourceCode instanceof WebInspector.Resource);
717 console.assert(!this._fullContentPopulated);
718 console.assert(!this._requestingScriptContent);
720 // We can assume this resource only has one script that starts at line/column 0.
721 var scripts = this._sourceCode.scripts;
722 console.assert(scripts.length === 1);
726 console.assert(scripts[0].range.startLine === 0);
727 console.assert(scripts[0].range.startColumn === 0);
729 function scriptContentAvailable(parameters)
731 var content = parameters.content;
732 delete this._requestingScriptContent;
734 // Abort if the full content populated while waiting for this async callback.
735 if (this._fullContentPopulated)
738 // This is the full content.
739 this._fullContentPopulated = true;
741 this._populateWithContent(content);
744 this._requestingScriptContent = true;
746 scripts[0].requestContent().then(scriptContentAvailable.bind(this));
749 _matchesSourceCodeLocation(sourceCodeLocation)
751 if (this._sourceCode instanceof WebInspector.SourceMapResource)
752 return sourceCodeLocation.displaySourceCode === this._sourceCode;
753 if (this._sourceCode instanceof WebInspector.Resource)
754 return sourceCodeLocation.sourceCode.url === this._sourceCode.url;
755 if (this._sourceCode instanceof WebInspector.Script)
756 return sourceCodeLocation.sourceCode === this._sourceCode;
760 _matchesBreakpoint(breakpoint)
762 console.assert(this._supportsDebugging);
763 if (this._sourceCode instanceof WebInspector.SourceMapResource)
764 return breakpoint.sourceCodeLocation.displaySourceCode === this._sourceCode;
765 if (this._sourceCode instanceof WebInspector.Resource)
766 return breakpoint.url === this._sourceCode.url;
767 if (this._sourceCode instanceof WebInspector.Script)
768 return breakpoint.url === this._sourceCode.url || breakpoint.scriptIdentifier === this._sourceCode.id;
774 if (this._sourceCode instanceof WebInspector.Resource)
775 return issue.url === this._sourceCode.url;
776 // FIXME: Support issues for Scripts based on id, not only by URL.
777 if (this._sourceCode instanceof WebInspector.Script)
778 return issue.url === this._sourceCode.url;
782 _issueWasAdded(event)
784 var issue = event.data.issue;
785 if (!this._matchesIssue(issue))
788 this._addIssue(issue);
793 // FIXME: Issue should have a SourceCodeLocation.
794 var sourceCodeLocation = this._sourceCode.createSourceCodeLocation(issue.lineNumber, issue.columnNumber);
795 var lineNumber = sourceCodeLocation.formattedLineNumber;
797 var lineNumberIssues = this._issuesLineNumberMap.get(lineNumber);
798 if (!lineNumberIssues) {
799 lineNumberIssues = [];
800 this._issuesLineNumberMap.set(lineNumber, lineNumberIssues);
803 // Avoid displaying duplicate issues on the same line.
804 for (var existingIssue of lineNumberIssues) {
805 if (existingIssue.columnNumber === issue.columnNumber && existingIssue.text === issue.text)
809 lineNumberIssues.push(issue);
811 if (issue.level === WebInspector.IssueMessage.Level.Error)
812 this.addStyleClassToLine(lineNumber, WebInspector.SourceCodeTextEditor.LineErrorStyleClassName);
813 else if (issue.level === WebInspector.IssueMessage.Level.Warning)
814 this.addStyleClassToLine(lineNumber, WebInspector.SourceCodeTextEditor.LineWarningStyleClassName);
816 console.error("Unknown issue level");
818 var widget = this._issueWidgetForLine(lineNumber);
820 if (issue.level === WebInspector.IssueMessage.Level.Error)
821 widget.widgetElement.classList.add(WebInspector.SourceCodeTextEditor.LineErrorStyleClassName);
822 else if (issue.level === WebInspector.IssueMessage.Level.Warning)
823 widget.widgetElement.classList.add(WebInspector.SourceCodeTextEditor.LineWarningStyleClassName);
825 this._updateIssueWidgetForIssues(widget, lineNumberIssues);
829 _issueWidgetForLine(lineNumber)
831 var widget = this._widgetMap.get(lineNumber);
835 widget = this.createWidgetForLine(lineNumber);
839 var widgetElement = widget.widgetElement;
840 widgetElement.classList.add("issue-widget", "inline");
841 widgetElement.addEventListener("click", this._handleWidgetClick.bind(this, widget, lineNumber));
843 this._widgetMap.set(lineNumber, widget);
848 _iconClassNameForIssueLevel(level)
850 if (level === WebInspector.IssueMessage.Level.Warning)
851 return "icon-warning";
853 console.assert(level === WebInspector.IssueMessage.Level.Error);
857 _updateIssueWidgetForIssues(widget, issues)
859 var widgetElement = widget.widgetElement;
860 widgetElement.removeChildren();
862 if (widgetElement.classList.contains("inline") || issues.length === 1) {
863 var arrowElement = widgetElement.appendChild(document.createElement("span"));
864 arrowElement.className = "arrow";
866 var iconElement = widgetElement.appendChild(document.createElement("span"));
867 iconElement.className = "icon";
869 var textElement = widgetElement.appendChild(document.createElement("span"));
870 textElement.className = "text";
872 if (issues.length === 1) {
873 iconElement.classList.add(this._iconClassNameForIssueLevel(issues[0].level));
874 textElement.textContent = issues[0].text;
877 var warningsCount = 0;
878 for (var issue of issues) {
879 if (issue.level === WebInspector.IssueMessage.Level.Error)
881 else if (issue.level === WebInspector.IssueMessage.Level.Warning)
885 if (warningsCount && errorsCount) {
886 iconElement.classList.add(this._iconClassNameForIssueLevel(issue.level));
887 textElement.textContent = WebInspector.UIString("%d Errors, %d Warnings").format(errorsCount, warningsCount);
888 } else if (errorsCount) {
889 iconElement.classList.add(this._iconClassNameForIssueLevel(issue.level));
890 textElement.textContent = WebInspector.UIString("%d Errors").format(errorsCount);
891 } else if (warningsCount) {
892 iconElement.classList.add(this._iconClassNameForIssueLevel(issue.level));
893 textElement.textContent = WebInspector.UIString("%d Warnings").format(warningsCount);
896 widget[WebInspector.SourceCodeTextEditor.WidgetContainsMultipleIssuesSymbol] = true;
899 for (var issue of issues) {
900 var iconElement = widgetElement.appendChild(document.createElement("span"));
901 iconElement.className = "icon";
902 iconElement.classList.add(this._iconClassNameForIssueLevel(issue.level));
904 var textElement = widgetElement.appendChild(document.createElement("span"));
905 textElement.className = "text";
906 textElement.textContent = issue.text;
908 widgetElement.appendChild(document.createElement("br"));
915 _isWidgetToggleable(widget)
917 if (widget[WebInspector.SourceCodeTextEditor.WidgetContainsMultipleIssuesSymbol])
920 if (!widget.widgetElement.classList.contains("inline"))
923 var textElement = widget.widgetElement.lastChild;
924 if (textElement.offsetWidth !== textElement.scrollWidth)
930 _handleWidgetClick(widget, lineNumber, event)
932 if (!this._isWidgetToggleable(widget))
935 widget.widgetElement.classList.toggle("inline");
937 var lineNumberIssues = this._issuesLineNumberMap.get(lineNumber);
938 this._updateIssueWidgetForIssues(widget, lineNumberIssues);
941 _breakpointInfoForBreakpoint(breakpoint)
943 return {resolved: breakpoint.resolved, disabled: breakpoint.disabled, autoContinue: breakpoint.autoContinue};
946 get _supportsDebugging()
948 if (this._sourceCode instanceof WebInspector.Resource)
949 return this._sourceCode.type === WebInspector.Resource.Type.Document || this._sourceCode.type === WebInspector.Resource.Type.Script;
950 if (this._sourceCode instanceof WebInspector.Script)
955 // TextEditor Delegate
957 textEditorBaseURL(textEditor)
959 return this._sourceCode.url;
962 textEditorShouldHideLineNumber(textEditor, lineNumber)
964 return lineNumber in this._invalidLineNumbers;
967 textEditorGutterContextMenu(textEditor, lineNumber, columnNumber, editorBreakpoints, event)
969 if (!this._supportsDebugging)
972 event.preventDefault();
974 let addBreakpoint = () => {
975 let data = this.textEditorBreakpointAdded(this, lineNumber, columnNumber);
976 this.setBreakpointInfoForLineAndColumn(data.lineNumber, data.columnNumber, data.breakpointInfo);
979 let contextMenu = WebInspector.ContextMenu.createFromEvent(event);
981 // Paused. Add Continue to Here option only if we have a script identifier for the location.
982 if (WebInspector.debuggerManager.paused) {
983 let editorLineInfo = {lineNumber, columnNumber};
984 let unformattedLineInfo = this._unformattedLineInfoForEditorLineInfo(editorLineInfo);
985 let sourceCodeLocation = this._sourceCode.createSourceCodeLocation(unformattedLineInfo.lineNumber, unformattedLineInfo.columnNumber);
988 if (sourceCodeLocation.sourceCode instanceof WebInspector.Script)
989 script = sourceCodeLocation.sourceCode;
990 else if (sourceCodeLocation.sourceCode instanceof WebInspector.Resource)
991 script = sourceCodeLocation.sourceCode.scriptForLocation(sourceCodeLocation);
994 contextMenu.appendItem(WebInspector.UIString("Continue to Here"), () => {
995 WebInspector.debuggerManager.continueToLocation(script.id, sourceCodeLocation.lineNumber, sourceCodeLocation.columnNumber);
997 contextMenu.appendSeparator();
1001 let breakpoints = [];
1002 for (let lineInfo of editorBreakpoints) {
1003 let breakpoint = this._breakpointForEditorLineInfo(lineInfo);
1004 console.assert(breakpoint);
1006 breakpoints.push(breakpoint);
1010 if (!breakpoints.length) {
1011 contextMenu.appendItem(WebInspector.UIString("Add Breakpoint"), addBreakpoint.bind(this));
1015 // Single breakpoint.
1016 if (breakpoints.length === 1) {
1017 WebInspector.breakpointPopoverController.appendContextMenuItems(contextMenu, breakpoints[0], event.target);
1019 if (!WebInspector.isShowingDebuggerTab()) {
1020 contextMenu.appendSeparator();
1021 contextMenu.appendItem(WebInspector.UIString("Reveal in Debugger Tab"), () => {
1022 WebInspector.showDebuggerTab(breakpoint);
1029 // Multiple breakpoints.
1030 let removeBreakpoints = () => {
1031 for (let breakpoint of breakpoints) {
1032 if (WebInspector.debuggerManager.isBreakpointRemovable(breakpoint))
1033 WebInspector.debuggerManager.removeBreakpoint(breakpoint);
1037 let shouldDisable = breakpoints.some((breakpoint) => !breakpoint.disabled);
1038 let toggleBreakpoints = (shouldDisable) => {
1039 for (let breakpoint of breakpoints)
1040 breakpoint.disabled = shouldDisable;
1044 contextMenu.appendItem(WebInspector.UIString("Disable Breakpoints"), toggleBreakpoints);
1046 contextMenu.appendItem(WebInspector.UIString("Enable Breakpoints"), toggleBreakpoints);
1047 contextMenu.appendItem(WebInspector.UIString("Delete Breakpoints"), removeBreakpoints);
1050 textEditorBreakpointAdded(textEditor, lineNumber, columnNumber)
1052 if (!this._supportsDebugging)
1055 var editorLineInfo = {lineNumber, columnNumber};
1056 var unformattedLineInfo = this._unformattedLineInfoForEditorLineInfo(editorLineInfo);
1057 var sourceCodeLocation = this._sourceCode.createSourceCodeLocation(unformattedLineInfo.lineNumber, unformattedLineInfo.columnNumber);
1058 var breakpoint = new WebInspector.Breakpoint(sourceCodeLocation);
1060 var lineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
1061 this._addBreakpointWithEditorLineInfo(breakpoint, lineInfo);
1063 this._ignoreBreakpointAddedBreakpoint = breakpoint;
1065 var shouldSkipEventDispatch = false;
1066 var shouldSpeculativelyResolveBreakpoint = true;
1067 WebInspector.debuggerManager.addBreakpoint(breakpoint, shouldSkipEventDispatch, shouldSpeculativelyResolveBreakpoint);
1068 delete this._ignoreBreakpointAddedBreakpoint;
1070 // Return the more accurate location and breakpoint info.
1072 breakpointInfo: this._breakpointInfoForBreakpoint(breakpoint),
1073 lineNumber: lineInfo.lineNumber,
1074 columnNumber: lineInfo.columnNumber
1078 textEditorBreakpointRemoved(textEditor, lineNumber, columnNumber)
1080 console.assert(this._supportsDebugging);
1081 if (!this._supportsDebugging)
1084 var lineInfo = {lineNumber, columnNumber};
1085 var breakpoint = this._breakpointForEditorLineInfo(lineInfo);
1086 console.assert(breakpoint);
1090 this._removeBreakpointWithEditorLineInfo(breakpoint, lineInfo);
1092 this._ignoreBreakpointRemovedBreakpoint = breakpoint;
1093 WebInspector.debuggerManager.removeBreakpoint(breakpoint);
1094 delete this._ignoreBreakpointAddedBreakpoint;
1097 textEditorBreakpointMoved(textEditor, oldLineNumber, oldColumnNumber, newLineNumber, newColumnNumber)
1099 console.assert(this._supportsDebugging);
1100 if (!this._supportsDebugging)
1103 var oldLineInfo = {lineNumber: oldLineNumber, columnNumber: oldColumnNumber};
1104 var breakpoint = this._breakpointForEditorLineInfo(oldLineInfo);
1105 console.assert(breakpoint);
1109 this._removeBreakpointWithEditorLineInfo(breakpoint, oldLineInfo);
1111 var newLineInfo = {lineNumber: newLineNumber, columnNumber: newColumnNumber};
1112 var unformattedNewLineInfo = this._unformattedLineInfoForEditorLineInfo(newLineInfo);
1113 this._ignoreLocationUpdateBreakpoint = breakpoint;
1114 breakpoint.sourceCodeLocation.update(this._sourceCode, unformattedNewLineInfo.lineNumber, unformattedNewLineInfo.columnNumber);
1115 delete this._ignoreLocationUpdateBreakpoint;
1117 var accurateNewLineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
1118 this._addBreakpointWithEditorLineInfo(breakpoint, accurateNewLineInfo);
1120 if (accurateNewLineInfo.lineNumber !== newLineInfo.lineNumber || accurateNewLineInfo.columnNumber !== newLineInfo.columnNumber)
1121 this.updateBreakpointLineAndColumn(newLineInfo.lineNumber, newLineInfo.columnNumber, accurateNewLineInfo.lineNumber, accurateNewLineInfo.columnNumber);
1124 textEditorBreakpointClicked(textEditor, lineNumber, columnNumber)
1126 console.assert(this._supportsDebugging);
1127 if (!this._supportsDebugging)
1130 var breakpoint = this._breakpointForEditorLineInfo({lineNumber, columnNumber});
1131 console.assert(breakpoint);
1135 breakpoint.cycleToNextMode();
1138 textEditorUpdatedFormatting(textEditor)
1140 this._ignoreAllBreakpointLocationUpdates = true;
1141 this._sourceCode.formatterSourceMap = this.formatterSourceMap;
1142 delete this._ignoreAllBreakpointLocationUpdates;
1144 // Always put the source map on both the Script and Resource if both exist. For example,
1145 // if this SourceCode is a Resource, then there might also be a Script. In the debugger,
1146 // the backend identifies call frames with Script line and column information, and the
1147 // Script needs the formatter source map to produce the proper display line and column.
1148 if (this._sourceCode instanceof WebInspector.Resource && !(this._sourceCode instanceof WebInspector.SourceMapResource)) {
1149 var scripts = this._sourceCode.scripts;
1150 for (var i = 0; i < scripts.length; ++i)
1151 scripts[i].formatterSourceMap = this.formatterSourceMap;
1152 } else if (this._sourceCode instanceof WebInspector.Script) {
1153 if (this._sourceCode.resource)
1154 this._sourceCode.resource.formatterSourceMap = this.formatterSourceMap;
1157 // Some breakpoints / issues may have moved, some might not have. Just go through
1158 // and remove and reinsert all the breakpoints / issues.
1160 var oldBreakpointMap = this._breakpointMap;
1161 this._breakpointMap = {};
1163 for (var lineNumber in oldBreakpointMap) {
1164 for (var columnNumber in oldBreakpointMap[lineNumber]) {
1165 var breakpoint = oldBreakpointMap[lineNumber][columnNumber];
1166 var newLineInfo = this._editorLineInfoForSourceCodeLocation(breakpoint.sourceCodeLocation);
1167 this._addBreakpointWithEditorLineInfo(breakpoint, newLineInfo);
1168 this.setBreakpointInfoForLineAndColumn(lineNumber, columnNumber, null);
1169 this.setBreakpointInfoForLineAndColumn(newLineInfo.lineNumber, newLineInfo.columnNumber, this._breakpointInfoForBreakpoint(breakpoint));
1173 this._reinsertAllIssues();
1178 for (var widget of this._widgetMap.values())
1181 this._widgetMap.clear();
1184 _reinsertAllIssues()
1186 this._issuesLineNumberMap.clear();
1187 this._clearWidgets();
1189 var issues = WebInspector.issueManager.issuesForSourceCode(this._sourceCode);
1190 for (var issue of issues) {
1191 console.assert(this._matchesIssue(issue));
1192 this._addIssue(issue);
1196 _debuggerDidPause(event)
1198 this._updateTokenTrackingControllerState();
1199 if (this._typeTokenAnnotator && this._typeTokenAnnotator.isActive())
1200 this._typeTokenAnnotator.refresh();
1201 if (this._basicBlockAnnotator && this._basicBlockAnnotator.isActive())
1202 this._basicBlockAnnotator.refresh();
1205 _debuggerDidResume(event)
1207 this._updateTokenTrackingControllerState();
1208 this._dismissPopover();
1209 if (this._typeTokenAnnotator && this._typeTokenAnnotator.isActive())
1210 this._typeTokenAnnotator.refresh();
1211 if (this._basicBlockAnnotator && this._basicBlockAnnotator.isActive())
1212 this._basicBlockAnnotator.refresh();
1215 _sourceCodeSourceMapAdded(event)
1217 WebInspector.notifications.addEventListener(WebInspector.Notification.GlobalModifierKeysDidChange, this._updateTokenTrackingControllerState, this);
1218 this._sourceCode.removeEventListener(WebInspector.SourceCode.Event.SourceMapAdded, this._sourceCodeSourceMapAdded, this);
1220 this._updateTokenTrackingControllerState();
1223 _updateTokenTrackingControllerState()
1225 var mode = WebInspector.CodeMirrorTokenTrackingController.Mode.None;
1226 if (WebInspector.debuggerManager.paused)
1227 mode = WebInspector.CodeMirrorTokenTrackingController.Mode.JavaScriptExpression;
1228 else if (this._typeTokenAnnotator && this._typeTokenAnnotator.isActive())
1229 mode = WebInspector.CodeMirrorTokenTrackingController.Mode.JavaScriptTypeInformation;
1230 else if (this._hasColorMarkers())
1231 mode = WebInspector.CodeMirrorTokenTrackingController.Mode.MarkedTokens;
1232 else if ((this._sourceCode instanceof WebInspector.SourceMapResource || this._sourceCode.sourceMaps.length !== 0) && WebInspector.modifierKeys.metaKey && !WebInspector.modifierKeys.altKey && !WebInspector.modifierKeys.shiftKey)
1233 mode = WebInspector.CodeMirrorTokenTrackingController.Mode.NonSymbolTokens;
1235 this.tokenTrackingController.enabled = mode !== WebInspector.CodeMirrorTokenTrackingController.Mode.None;
1237 if (mode === this.tokenTrackingController.mode)
1241 case WebInspector.CodeMirrorTokenTrackingController.Mode.MarkedTokens:
1242 this.tokenTrackingController.mouseOverDelayDuration = 0;
1243 this.tokenTrackingController.mouseOutReleaseDelayDuration = 0;
1245 case WebInspector.CodeMirrorTokenTrackingController.Mode.NonSymbolTokens:
1246 this.tokenTrackingController.mouseOverDelayDuration = 0;
1247 this.tokenTrackingController.mouseOutReleaseDelayDuration = 0;
1248 this.tokenTrackingController.classNameForHighlightedRange = WebInspector.CodeMirrorTokenTrackingController.JumpToSymbolHighlightStyleClassName;
1249 this._dismissPopover();
1251 case WebInspector.CodeMirrorTokenTrackingController.Mode.JavaScriptExpression:
1252 case WebInspector.CodeMirrorTokenTrackingController.Mode.JavaScriptTypeInformation:
1253 this.tokenTrackingController.mouseOverDelayDuration = WebInspector.SourceCodeTextEditor.DurationToMouseOverTokenToMakeHoveredToken;
1254 this.tokenTrackingController.mouseOutReleaseDelayDuration = WebInspector.SourceCodeTextEditor.DurationToMouseOutOfHoveredTokenToRelease;
1255 this.tokenTrackingController.classNameForHighlightedRange = WebInspector.SourceCodeTextEditor.HoveredExpressionHighlightStyleClassName;
1259 this.tokenTrackingController.mode = mode;
1264 for (var marker of this.markers) {
1265 if (marker.type === WebInspector.TextMarker.Type.Color)
1271 // CodeMirrorTokenTrackingController Delegate
1273 tokenTrackingControllerCanReleaseHighlightedRange(tokenTrackingController, element)
1278 if (!window.getSelection().isCollapsed && this._popover.element.contains(window.getSelection().anchorNode))
1284 tokenTrackingControllerHighlightedRangeReleased(tokenTrackingController, forceHide = false)
1286 if (forceHide || !this._mouseIsOverPopover)
1287 this._dismissPopover();
1290 tokenTrackingControllerHighlightedRangeWasClicked(tokenTrackingController)
1292 if (this.tokenTrackingController.mode !== WebInspector.CodeMirrorTokenTrackingController.Mode.NonSymbolTokens)
1295 // Links are handled by TextEditor.
1296 if (/\blink\b/.test(this.tokenTrackingController.candidate.hoveredToken.type))
1299 var sourceCodeLocation = this._sourceCodeLocationForEditorPosition(this.tokenTrackingController.candidate.hoveredTokenRange.start);
1300 if (this.sourceCode instanceof WebInspector.SourceMapResource)
1301 WebInspector.showOriginalOrFormattedSourceCodeLocation(sourceCodeLocation);
1303 WebInspector.showSourceCodeLocation(sourceCodeLocation);
1306 tokenTrackingControllerNewHighlightCandidate(tokenTrackingController, candidate)
1308 if (this.tokenTrackingController.mode === WebInspector.CodeMirrorTokenTrackingController.Mode.NonSymbolTokens) {
1309 this.tokenTrackingController.highlightRange(candidate.hoveredTokenRange);
1313 if (this.tokenTrackingController.mode === WebInspector.CodeMirrorTokenTrackingController.Mode.JavaScriptExpression) {
1314 this._tokenTrackingControllerHighlightedJavaScriptExpression(candidate);
1318 if (this.tokenTrackingController.mode === WebInspector.CodeMirrorTokenTrackingController.Mode.JavaScriptTypeInformation) {
1319 this._tokenTrackingControllerHighlightedJavaScriptTypeInformation(candidate);
1323 if (this.tokenTrackingController.mode === WebInspector.CodeMirrorTokenTrackingController.Mode.MarkedTokens) {
1324 var markers = this.markersAtPosition(candidate.hoveredTokenRange.start);
1325 if (markers.length > 0)
1326 this._tokenTrackingControllerHighlightedMarkedExpression(candidate, markers);
1328 this._dismissEditingController();
1332 tokenTrackingControllerMouseOutOfHoveredMarker(tokenTrackingController, hoveredMarker)
1334 this._dismissEditingController();
1337 _tokenTrackingControllerHighlightedJavaScriptExpression(candidate)
1339 console.assert(candidate.expression);
1341 function populate(error, result, wasThrown)
1343 if (error || wasThrown)
1346 if (candidate !== this.tokenTrackingController.candidate)
1349 var data = WebInspector.RemoteObject.fromPayload(result);
1350 switch (data.type) {
1352 this._showPopoverForFunction(data);
1355 if (data.subtype === "null" || data.subtype === "regexp")
1356 this._showPopoverWithFormattedValue(data);
1358 this._showPopoverForObject(data);
1365 this._showPopoverWithFormattedValue(data);
1370 var expression = appendWebInspectorSourceURL(candidate.expression);
1372 if (WebInspector.debuggerManager.activeCallFrame) {
1373 DebuggerAgent.evaluateOnCallFrame.invoke({callFrameId: WebInspector.debuggerManager.activeCallFrame.id, expression, objectGroup: "popover", doNotPauseOnExceptionsAndMuteConsole: true}, populate.bind(this));
1377 // No call frame available. Use the main page's context.
1378 RuntimeAgent.evaluate.invoke({expression, objectGroup: "popover", doNotPauseOnExceptionsAndMuteConsole: true}, populate.bind(this));
1381 _tokenTrackingControllerHighlightedJavaScriptTypeInformation(candidate)
1383 console.assert(candidate.expression);
1385 var sourceCode = this._sourceCode;
1386 var sourceID = sourceCode instanceof WebInspector.Script ? sourceCode.id : sourceCode.scripts[0].id;
1387 var range = candidate.hoveredTokenRange;
1388 var offset = this.currentPositionToOriginalOffset({line: range.start.line, ch: range.start.ch});
1390 var allRequests = [{
1391 typeInformationDescriptor: WebInspector.ScriptSyntaxTree.TypeProfilerSearchDescriptor.NormalExpression,
1396 function handler(error, allTypes) {
1400 if (candidate !== this.tokenTrackingController.candidate)
1403 console.assert(allTypes.length === 1);
1404 if (!allTypes.length)
1407 var typeDescription = WebInspector.TypeDescription.fromPayload(allTypes[0]);
1408 if (typeDescription.valid) {
1409 var popoverTitle = WebInspector.TypeTokenView.titleForPopover(WebInspector.TypeTokenView.TitleType.Variable, candidate.expression);
1410 this.showPopoverForTypes(typeDescription, null, popoverTitle);
1414 RuntimeAgent.getRuntimeTypesForVariablesAtOffsets(allRequests, handler.bind(this));
1417 _showPopover(content, bounds)
1419 console.assert(this.tokenTrackingController.candidate || bounds);
1421 var shouldHighlightRange = false;
1422 var candidate = this.tokenTrackingController.candidate;
1423 // If bounds is falsey, this is a popover introduced from a hover event.
1424 // Otherwise, this is called from TypeTokenAnnotator.
1429 var rects = this.rectsForRange(candidate.hoveredTokenRange);
1430 bounds = WebInspector.Rect.unionOfRects(rects);
1432 shouldHighlightRange = true;
1435 content.classList.add(WebInspector.SourceCodeTextEditor.PopoverDebuggerContentStyleClassName);
1437 this._popover = this._popover || new WebInspector.Popover(this);
1438 this._popover.presentNewContentWithFrame(content, bounds.pad(5), [WebInspector.RectEdge.MIN_Y, WebInspector.RectEdge.MAX_Y, WebInspector.RectEdge.MAX_X]);
1439 if (shouldHighlightRange)
1440 this.tokenTrackingController.highlightRange(candidate.expressionRange);
1442 this._trackPopoverEvents();
1445 _showPopoverForFunction(data)
1447 var candidate = this.tokenTrackingController.candidate;
1449 function didGetDetails(error, response)
1452 console.error(error);
1453 this._dismissPopover();
1457 // Nothing to do if the token has changed since the time we
1458 // asked for the function details from the backend.
1459 if (candidate !== this.tokenTrackingController.candidate)
1462 var wrapper = document.createElement("div");
1463 wrapper.className = "body formatted-function";
1464 wrapper.textContent = data.description;
1466 var content = document.createElement("div");
1467 content.className = "function";
1469 var title = content.appendChild(document.createElement("div"));
1470 title.className = "title";
1471 title.textContent = response.name || response.inferredName || response.displayName || WebInspector.UIString("(anonymous function)");
1473 content.appendChild(wrapper);
1475 this._showPopover(content);
1477 DebuggerAgent.getFunctionDetails(data.objectId, didGetDetails.bind(this));
1480 _showPopoverForObject(data)
1482 var content = document.createElement("div");
1483 content.className = "object expandable";
1485 var titleElement = document.createElement("div");
1486 titleElement.className = "title";
1487 titleElement.textContent = data.description;
1488 content.appendChild(titleElement);
1490 if (data.subtype === "node") {
1491 data.pushNodeToFrontend(function(nodeId) {
1495 var domNode = WebInspector.domTreeManager.nodeForId(nodeId);
1496 if (!domNode.ownerDocument)
1499 var goToButton = titleElement.appendChild(WebInspector.createGoToArrowButton());
1500 goToButton.addEventListener("click", function() {
1501 WebInspector.domTreeManager.inspectElement(nodeId);
1506 // FIXME: If this is a variable, it would be nice to put the variable name in the PropertyPath.
1507 var objectTree = new WebInspector.ObjectTreeView(data);
1508 objectTree.showOnlyProperties();
1509 objectTree.expand();
1511 var bodyElement = content.appendChild(document.createElement("div"));
1512 bodyElement.className = "body";
1513 bodyElement.appendChild(objectTree.element);
1515 // Show the popover once we have the first set of properties for the object.
1516 var candidate = this.tokenTrackingController.candidate;
1517 objectTree.addEventListener(WebInspector.ObjectTreeView.Event.Updated, function() {
1518 if (candidate === this.tokenTrackingController.candidate)
1519 this._showPopover(content);
1520 objectTree.removeEventListener(null, null, this);
1524 _showPopoverWithFormattedValue(remoteObject)
1526 var content = WebInspector.FormattedValue.createElementForRemoteObject(remoteObject);
1527 this._showPopover(content);
1530 willDismissPopover(popover)
1532 this.tokenTrackingController.removeHighlightedRange();
1534 RuntimeAgent.releaseObjectGroup("popover");
1542 this._popover.dismiss();
1544 if (this._popoverEventListeners && this._popoverEventListenersAreRegistered) {
1545 this._popoverEventListenersAreRegistered = false;
1546 this._popoverEventListeners.unregister();
1550 _trackPopoverEvents()
1552 if (!this._popoverEventListeners)
1553 this._popoverEventListeners = new WebInspector.EventListenerSet(this, "Popover listeners");
1554 if (!this._popoverEventListenersAreRegistered) {
1555 this._popoverEventListenersAreRegistered = true;
1556 this._popoverEventListeners.register(this._popover.element, "mouseover", this._popoverMouseover);
1557 this._popoverEventListeners.register(this._popover.element, "mouseout", this._popoverMouseout);
1558 this._popoverEventListeners.install();
1562 _popoverMouseover(event)
1564 this._mouseIsOverPopover = true;
1567 _popoverMouseout(event)
1569 this._mouseIsOverPopover = this._popover.element.contains(event.relatedTarget);
1572 _updateEditableMarkers(range)
1574 this.createColorMarkers(range);
1575 this.createGradientMarkers(range);
1576 this.createCubicBezierMarkers(range);
1578 this._updateTokenTrackingControllerState();
1581 _tokenTrackingControllerHighlightedMarkedExpression(candidate, markers)
1583 // Look for the outermost editable marker.
1585 for (var marker of markers) {
1586 if (!marker.range || (marker.type !== WebInspector.TextMarker.Type.Color && marker.type !== WebInspector.TextMarker.Type.Gradient && marker.type !== WebInspector.TextMarker.Type.CubicBezier))
1589 if (!editableMarker || (marker.range.startLine < editableMarker.range.startLine || (marker.range.startLine === editableMarker.range.startLine && marker.range.startColumn < editableMarker.range.startColumn)))
1590 editableMarker = marker;
1593 if (!editableMarker) {
1594 this.tokenTrackingController.hoveredMarker = null;
1598 if (this.tokenTrackingController.hoveredMarker === editableMarker)
1601 this._dismissEditingController();
1603 this.tokenTrackingController.hoveredMarker = editableMarker;
1605 this._editingController = this.editingControllerForMarker(editableMarker);
1607 if (marker.type === WebInspector.TextMarker.Type.Color) {
1608 var color = this._editingController.value;
1609 if (!color || !color.valid) {
1610 editableMarker.clear();
1611 delete this._editingController;
1616 this._editingController.delegate = this;
1617 this._editingController.presentHoverMenu();
1620 _dismissEditingController(discrete)
1622 if (this._editingController)
1623 this._editingController.dismissHoverMenu(discrete);
1625 this.tokenTrackingController.hoveredMarker = null;
1626 delete this._editingController;
1629 // CodeMirrorEditingController Delegate
1631 editingControllerDidStartEditing(editingController)
1633 // We can pause the token tracking controller during editing, it will be reset
1634 // to the expected state by calling _updateEditableMarkers() in the
1635 // editingControllerDidFinishEditing delegate.
1636 this.tokenTrackingController.enabled = false;
1638 // We clear the marker since we'll reset it after editing.
1639 editingController.marker.clear();
1641 // We ignore content changes made as a result of color editing.
1642 this._ignoreContentDidChange++;
1645 editingControllerDidFinishEditing(editingController)
1647 this._updateEditableMarkers(editingController.range);
1649 this._ignoreContentDidChange--;
1651 delete this._editingController;
1654 _setTypeTokenAnnotatorEnabledState(shouldActivate)
1656 console.assert(this._typeTokenAnnotator);
1657 if (!this._typeTokenAnnotator)
1660 if (shouldActivate) {
1661 console.assert(this.visible, "Annotators should not be enabled if the TextEditor is not visible");
1663 RuntimeAgent.enableTypeProfiler();
1665 this._typeTokenAnnotator.reset();
1666 if (this._basicBlockAnnotator) {
1667 console.assert(!this._basicBlockAnnotator.isActive());
1668 this._basicBlockAnnotator.reset();
1671 if (!this._typeTokenScrollHandler)
1672 this._enableScrollEventsForTypeTokenAnnotator();
1674 // Because we disable type profiling when exiting the inspector, there is no need to call
1675 // RuntimeAgent.disableTypeProfiler() here. If we were to call it here, JavaScriptCore would
1676 // compile out all the necessary type profiling information, so if a user were to quickly press then
1677 // unpress the type profiling button, we wouldn't be able to re-show type information which would
1678 // provide a confusing user experience.
1680 this._typeTokenAnnotator.clear();
1681 if (this._basicBlockAnnotator)
1682 this._basicBlockAnnotator.clear();
1684 if (this._typeTokenScrollHandler)
1685 this._disableScrollEventsForTypeTokenAnnotator();
1688 WebInspector.showJavaScriptTypeInformationSetting.value = shouldActivate;
1690 this._updateTokenTrackingControllerState();
1693 _getAssociatedScript()
1696 // FIXME: This needs to me modified to work with HTML files with inline script tags.
1697 if (this._sourceCode instanceof WebInspector.Script)
1698 script = this._sourceCode;
1699 else if (this._sourceCode instanceof WebInspector.Resource && this._sourceCode.type === WebInspector.Resource.Type.Script && this._sourceCode.scripts.length)
1700 script = this._sourceCode.scripts[0];
1704 _makeTypeTokenAnnotator()
1706 // COMPATIBILITY (iOS 8): Runtime.getRuntimeTypesForVariablesAtOffsets did not exist yet.
1707 if (!RuntimeAgent.getRuntimeTypesForVariablesAtOffsets)
1710 var script = this._getAssociatedScript();
1714 this._typeTokenAnnotator = new WebInspector.TypeTokenAnnotator(this, script);
1717 _makeBasicBlockAnnotator()
1719 // COMPATIBILITY (iOS 8): Runtime.getBasicBlocks did not exist yet.
1720 if (!RuntimeAgent.getBasicBlocks)
1723 var script = this._getAssociatedScript();
1727 this._basicBlockAnnotator = new WebInspector.BasicBlockAnnotator(this, script);
1730 _enableScrollEventsForTypeTokenAnnotator()
1732 // Pause updating type tokens while scrolling to prevent frame loss.
1733 console.assert(!this._typeTokenScrollHandler);
1734 this._typeTokenScrollHandler = this._makeTypeTokenScrollEventHandler();
1735 this.addScrollHandler(this._typeTokenScrollHandler);
1738 _disableScrollEventsForTypeTokenAnnotator()
1740 console.assert(this._typeTokenScrollHandler);
1741 this.removeScrollHandler(this._typeTokenScrollHandler);
1742 this._typeTokenScrollHandler = null;
1745 _makeTypeTokenScrollEventHandler()
1747 var timeoutIdentifier = null;
1748 function scrollHandler()
1750 if (timeoutIdentifier)
1751 clearTimeout(timeoutIdentifier);
1753 if (this._typeTokenAnnotator)
1754 this._typeTokenAnnotator.pause();
1755 if (this._basicBlockAnnotator)
1756 this._basicBlockAnnotator.pause();
1759 timeoutIdentifier = setTimeout(function() {
1760 timeoutIdentifier = null;
1761 if (this._typeTokenAnnotator)
1762 this._typeTokenAnnotator.resume();
1763 if (this._basicBlockAnnotator)
1764 this._basicBlockAnnotator.resume();
1765 }.bind(this), WebInspector.SourceCodeTextEditor.DurationToUpdateTypeTokensAfterScrolling);
1768 return scrollHandler.bind(this);
1772 WebInspector.SourceCodeTextEditor.LineErrorStyleClassName = "error";
1773 WebInspector.SourceCodeTextEditor.LineWarningStyleClassName = "warning";
1774 WebInspector.SourceCodeTextEditor.PopoverDebuggerContentStyleClassName = "debugger-popover-content";
1775 WebInspector.SourceCodeTextEditor.HoveredExpressionHighlightStyleClassName = "hovered-expression-highlight";
1776 WebInspector.SourceCodeTextEditor.DurationToMouseOverTokenToMakeHoveredToken = 500;
1777 WebInspector.SourceCodeTextEditor.DurationToMouseOutOfHoveredTokenToRelease = 1000;
1778 WebInspector.SourceCodeTextEditor.DurationToUpdateTypeTokensAfterScrolling = 100;
1779 WebInspector.SourceCodeTextEditor.WidgetContainsMultipleIssuesSymbol = Symbol("source-code-widget-contains-multiple-issues");
1781 WebInspector.SourceCodeTextEditor.Event = {
1782 ContentWillPopulate: "source-code-text-editor-content-will-populate",
1783 ContentDidPopulate: "source-code-text-editor-content-did-populate"