2 * Copyright (C) 2011 Google 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 are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * @extends {WebInspector.UISourceCodeFrame}
34 * @param {!WebInspector.SourcesPanel} scriptsPanel
35 * @param {!WebInspector.UISourceCode} uiSourceCode
37 WebInspector
.JavaScriptSourceFrame = function(scriptsPanel
, uiSourceCode
)
39 this._scriptsPanel
= scriptsPanel
;
40 this._breakpointManager
= WebInspector
.breakpointManager
;
41 this._uiSourceCode
= uiSourceCode
;
43 WebInspector
.UISourceCodeFrame
.call(this, uiSourceCode
);
44 if (uiSourceCode
.project().type() === WebInspector
.projectTypes
.Debugger
)
45 this.element
.classList
.add("source-frame-debugger-script");
47 this._popoverHelper
= new WebInspector
.ObjectPopoverHelper(scriptsPanel
.element
,
48 this._getPopoverAnchor
.bind(this), this._resolveObjectForPopover
.bind(this), this._onHidePopover
.bind(this), true);
50 this.textEditor
.element
.addEventListener("keydown", this._onKeyDown
.bind(this), true);
52 this.textEditor
.addEventListener(WebInspector
.CodeMirrorTextEditor
.Events
.GutterClick
, this._handleGutterClick
.bind(this), this);
54 this._breakpointManager
.addEventListener(WebInspector
.BreakpointManager
.Events
.BreakpointAdded
, this._breakpointAdded
, this);
55 this._breakpointManager
.addEventListener(WebInspector
.BreakpointManager
.Events
.BreakpointRemoved
, this._breakpointRemoved
, this);
57 WebInspector
.presentationConsoleMessageHelper
.addConsoleMessageEventListener(WebInspector
.PresentationConsoleMessageHelper
.Events
.ConsoleMessageAdded
, this._uiSourceCode
, this._consoleMessageAdded
, this);
58 WebInspector
.presentationConsoleMessageHelper
.addConsoleMessageEventListener(WebInspector
.PresentationConsoleMessageHelper
.Events
.ConsoleMessageRemoved
, this._uiSourceCode
, this._consoleMessageRemoved
, this);
59 WebInspector
.presentationConsoleMessageHelper
.addConsoleMessageEventListener(WebInspector
.PresentationConsoleMessageHelper
.Events
.ConsoleMessagesCleared
, this._uiSourceCode
, this._consoleMessagesCleared
, this);
60 this._uiSourceCode
.addEventListener(WebInspector
.UISourceCode
.Events
.SourceMappingChanged
, this._onSourceMappingChanged
, this);
61 this._uiSourceCode
.addEventListener(WebInspector
.UISourceCode
.Events
.WorkingCopyChanged
, this._workingCopyChanged
, this);
62 this._uiSourceCode
.addEventListener(WebInspector
.UISourceCode
.Events
.WorkingCopyCommitted
, this._workingCopyCommitted
, this);
63 this._uiSourceCode
.addEventListener(WebInspector
.UISourceCode
.Events
.TitleChanged
, this._showBlackboxInfobarIfNeeded
, this);
65 /** @type {!Map.<!WebInspector.Target, !WebInspector.ResourceScriptFile>}*/
66 this._scriptFileForTarget
= new Map();
67 this._registerShortcuts();
68 var targets
= WebInspector
.targetManager
.targets();
69 for (var i
= 0; i
< targets
.length
; ++i
) {
70 var scriptFile
= WebInspector
.debuggerWorkspaceBinding
.scriptFile(uiSourceCode
, targets
[i
]);
72 this._updateScriptFile(targets
[i
]);
75 if (this._scriptFileForTarget
.size
|| uiSourceCode
.extension() === "js")
76 this._compiler
= new WebInspector
.JavaScriptCompiler(this);
78 WebInspector
.moduleSetting("skipStackFramesPattern").addChangeListener(this._showBlackboxInfobarIfNeeded
, this);
79 WebInspector
.moduleSetting("skipContentScripts").addChangeListener(this._showBlackboxInfobarIfNeeded
, this);
80 this._showBlackboxInfobarIfNeeded();
81 /** @type {!Map.<number, !Element>} */
82 this._valueWidgets
= new Map();
85 WebInspector
.JavaScriptSourceFrame
.prototype = {
86 _updateInfobars: function()
88 this.attachInfobars([this._blackboxInfobar
, this._divergedInfobar
]);
91 _showDivergedInfobar: function()
93 if (this._uiSourceCode
.contentType() !== WebInspector
.resourceTypes
.Script
)
96 if (this._divergedInfobar
)
97 this._divergedInfobar
.dispose();
99 var infobar
= new WebInspector
.UISourceCodeFrame
.Infobar(WebInspector
.Infobar
.Type
.Warning
, WebInspector
.UIString("Workspace mapping mismatch"));
100 this._divergedInfobar
= infobar
;
102 var fileURL
= this._uiSourceCode
.originURL();
103 infobar
.createDetailsRowMessage(WebInspector
.UIString("The content of this file on the file system:\u00a0")).appendChild(
104 WebInspector
.linkifyURLAsNode(fileURL
, fileURL
, "source-frame-infobar-details-url", true));
106 var scriptURL
= WebInspector
.networkMapping
.networkURL(this._uiSourceCode
);
107 infobar
.createDetailsRowMessage(WebInspector
.UIString("does not match the loaded script:\u00a0")).appendChild(
108 WebInspector
.linkifyURLAsNode(scriptURL
, scriptURL
, "source-frame-infobar-details-url", true));
110 infobar
.createDetailsRowMessage();
111 infobar
.createDetailsRowMessage(WebInspector
.UIString("Possible solutions are:"));
113 if (WebInspector
.moduleSetting("cacheDisabled").get())
114 infobar
.createDetailsRowMessage(" - ").createTextChild(WebInspector
.UIString("Reload inspected page"));
116 infobar
.createDetailsRowMessage(" - ").createTextChild(WebInspector
.UIString("Check \"Disable cache\" in settings and reload inspected page (recommended setup for authoring and debugging)"));
117 infobar
.createDetailsRowMessage(" - ").createTextChild(WebInspector
.UIString("Check that your file and script are both loaded from the correct source and their contents match"));
119 this._updateInfobars();
122 _hideDivergedInfobar: function()
124 if (!this._divergedInfobar
)
126 this._divergedInfobar
.dispose();
127 delete this._divergedInfobar
;
130 _showBlackboxInfobarIfNeeded: function()
132 var contentType
= this._uiSourceCode
.contentType();
133 if (contentType
!== WebInspector
.resourceTypes
.Script
&& contentType
!== WebInspector
.resourceTypes
.Document
)
135 var projectType
= this._uiSourceCode
.project().type();
136 if (projectType
=== WebInspector
.projectTypes
.Snippets
)
138 var networkURL
= WebInspector
.networkMapping
.networkURL(this._uiSourceCode
);
139 var url
= projectType
=== WebInspector
.projectTypes
.Formatter
? this._uiSourceCode
.originURL() : networkURL
;
140 var isContentScript
= projectType
=== WebInspector
.projectTypes
.ContentScripts
;
141 if (!WebInspector
.BlackboxSupport
.isBlackboxed(url
, isContentScript
)) {
142 this._hideBlackboxInfobar();
146 if (this._blackboxInfobar
)
147 this._blackboxInfobar
.dispose();
149 var infobar
= new WebInspector
.UISourceCodeFrame
.Infobar(WebInspector
.Infobar
.Type
.Warning
, WebInspector
.UIString("This script is blackboxed in debugger"));
150 this._blackboxInfobar
= infobar
;
152 infobar
.createDetailsRowMessage(WebInspector
.UIString("Debugger will skip stepping through this script, and will not stop on exceptions"));
153 infobar
.createDetailsRowMessage();
154 infobar
.createDetailsRowMessage(WebInspector
.UIString("Possible ways to cancel this behavior are:"));
156 infobar
.createDetailsRowMessage(" - ").createTextChild(WebInspector
.UIString("Press \"%s\" button in settings", WebInspector
.manageBlackboxingButtonLabel()));
157 var unblackboxLink
= infobar
.createDetailsRowMessage(" - ").createChild("span", "link");
158 unblackboxLink
.textContent
= WebInspector
.UIString("Unblackbox this script");
159 unblackboxLink
.addEventListener("click", unblackbox
, false);
161 function unblackbox()
163 WebInspector
.BlackboxSupport
.unblackbox(url
, isContentScript
);
166 this._updateInfobars();
169 _hideBlackboxInfobar: function()
171 if (!this._blackboxInfobar
)
173 this._blackboxInfobar
.dispose();
174 delete this._blackboxInfobar
;
177 _registerShortcuts: function()
179 var shortcutKeys
= WebInspector
.ShortcutsScreen
.SourcesPanelShortcuts
;
180 for (var i
= 0; i
< shortcutKeys
.EvaluateSelectionInConsole
.length
; ++i
) {
181 var keyDescriptor
= shortcutKeys
.EvaluateSelectionInConsole
[i
];
182 this.addShortcut(keyDescriptor
.key
, this._evaluateSelectionInConsole
.bind(this));
184 for (var i
= 0; i
< shortcutKeys
.AddSelectionToWatch
.length
; ++i
) {
185 var keyDescriptor
= shortcutKeys
.AddSelectionToWatch
[i
];
186 this.addShortcut(keyDescriptor
.key
, this._addCurrentSelectionToWatch
.bind(this));
190 _addCurrentSelectionToWatch: function()
192 var textSelection
= this.textEditor
.selection();
193 if (textSelection
&& !textSelection
.isEmpty())
194 this._innerAddToWatch(this.textEditor
.copyRange(textSelection
));
199 * @param {string} expression
201 _innerAddToWatch: function(expression
)
203 this._scriptsPanel
.addToWatch(expression
);
209 _evaluateSelectionInConsole: function()
211 var selection
= this.textEditor
.selection();
212 if (!selection
|| selection
.isEmpty())
214 this._evaluateInConsole(this.textEditor
.copyRange(selection
));
219 * @param {string} expression
221 _evaluateInConsole: function(expression
)
223 var currentExecutionContext
= WebInspector
.context
.flavor(WebInspector
.ExecutionContext
);
224 if (currentExecutionContext
)
225 WebInspector
.ConsoleModel
.evaluateCommandInConsole(currentExecutionContext
, expression
);
233 WebInspector
.UISourceCodeFrame
.prototype.wasShown
.call(this);
234 if (this._executionLocation
&& this.loaded
) {
235 // We need CodeMirrorTextEditor to be initialized prior to this call. @see crbug.com/499889
236 setImmediate(this._generateValuesInSource
.bind(this));
245 WebInspector
.UISourceCodeFrame
.prototype.willHide
.call(this);
246 this._popoverHelper
.hidePopover();
249 onUISourceCodeContentChanged: function()
251 this._removeAllBreakpoints();
252 WebInspector
.UISourceCodeFrame
.prototype.onUISourceCodeContentChanged
.call(this);
255 onTextChanged: function(oldRange
, newRange
)
257 this._scriptsPanel
.setIgnoreExecutionLineEvents(true);
258 WebInspector
.UISourceCodeFrame
.prototype.onTextChanged
.call(this, oldRange
, newRange
);
259 this._scriptsPanel
.setIgnoreExecutionLineEvents(false);
261 this._compiler
.scheduleCompile();
264 populateLineGutterContextMenu: function(contextMenu
, lineNumber
)
266 var uiLocation
= new WebInspector
.UILocation(this._uiSourceCode
, lineNumber
, 0);
267 this._scriptsPanel
.appendUILocationItems(contextMenu
, uiLocation
);
268 var breakpoint
= this._breakpointManager
.findBreakpointOnLine(this._uiSourceCode
, lineNumber
);
270 // This row doesn't have a breakpoint: We want to show Add Breakpoint and Add and Edit Breakpoint.
271 contextMenu
.appendItem(WebInspector
.UIString
.capitalize("Add ^breakpoint"), this._createNewBreakpoint
.bind(this, lineNumber
, 0, "", true));
272 contextMenu
.appendItem(WebInspector
.UIString
.capitalize("Add ^conditional ^breakpoint…"), this._editBreakpointCondition
.bind(this, lineNumber
));
274 // This row has a breakpoint, we want to show edit and remove breakpoint, and either disable or enable.
275 contextMenu
.appendItem(WebInspector
.UIString
.capitalize("Remove ^breakpoint"), breakpoint
.remove
.bind(breakpoint
));
276 contextMenu
.appendItem(WebInspector
.UIString
.capitalize("Edit ^breakpoint…"), this._editBreakpointCondition
.bind(this, lineNumber
, breakpoint
));
277 if (breakpoint
.enabled())
278 contextMenu
.appendItem(WebInspector
.UIString
.capitalize("Disable ^breakpoint"), breakpoint
.setEnabled
.bind(breakpoint
, false));
280 contextMenu
.appendItem(WebInspector
.UIString
.capitalize("Enable ^breakpoint"), breakpoint
.setEnabled
.bind(breakpoint
, true));
284 populateTextAreaContextMenu: function(contextMenu
, lineNumber
, columnNumber
)
286 var textSelection
= this.textEditor
.selection();
287 if (textSelection
&& !textSelection
.isEmpty()) {
288 var selection
= this.textEditor
.copyRange(textSelection
);
289 var addToWatchLabel
= WebInspector
.UIString
.capitalize("Add to ^watch");
290 contextMenu
.appendItem(addToWatchLabel
, this._innerAddToWatch
.bind(this, selection
));
291 var evaluateLabel
= WebInspector
.UIString
.capitalize("Evaluate in ^console");
292 contextMenu
.appendItem(evaluateLabel
, this._evaluateInConsole
.bind(this, selection
));
293 contextMenu
.appendSeparator();
297 * @this {WebInspector.JavaScriptSourceFrame}
298 * @param {!WebInspector.ResourceScriptFile} scriptFile
300 function addSourceMapURL(scriptFile
)
302 WebInspector
.AddSourceMapURLDialog
.show(this.element
, addSourceMapURLDialogCallback
.bind(null, scriptFile
));
306 * @param {!WebInspector.ResourceScriptFile} scriptFile
307 * @param {string} url
309 function addSourceMapURLDialogCallback(scriptFile
, url
)
313 scriptFile
.addSourceMapURL(url
);
316 WebInspector
.UISourceCodeFrame
.prototype.populateTextAreaContextMenu
.call(this, contextMenu
, lineNumber
, columnNumber
);
318 if (this._uiSourceCode
.project().type() === WebInspector
.projectTypes
.Network
&& WebInspector
.moduleSetting("jsSourceMapsEnabled").get()) {
319 if (this._scriptFileForTarget
.size
) {
320 var scriptFile
= this._scriptFileForTarget
.valuesArray()[0];
321 var addSourceMapURLLabel
= WebInspector
.UIString
.capitalize("Add ^source ^map\u2026");
322 contextMenu
.appendItem(addSourceMapURLLabel
, addSourceMapURL
.bind(this, scriptFile
));
323 contextMenu
.appendSeparator();
328 _workingCopyChanged: function(event
)
330 if (this._supportsEnabledBreakpointsWhileEditing() || this._scriptFileForTarget
.size
)
333 if (this._uiSourceCode
.isDirty())
334 this._muteBreakpointsWhileEditing();
336 this._restoreBreakpointsAfterEditing();
339 _workingCopyCommitted: function(event
)
341 if (this._supportsEnabledBreakpointsWhileEditing())
344 if (!this._scriptFileForTarget
.size
) {
345 this._restoreBreakpointsAfterEditing();
350 var liveEditErrorData
;
352 var succeededEdits
= 0;
356 * @this {WebInspector.JavaScriptSourceFrame}
357 * @param {?string} error
358 * @param {!DebuggerAgent.SetScriptSourceError=} errorData
359 * @param {!WebInspector.Script=} script
361 function liveEditCallback(error
, errorData
, script
)
363 this._scriptsPanel
.setIgnoreExecutionLineEvents(false);
365 liveEditError
= error
;
366 liveEditErrorData
= errorData
;
367 contextScript
= script
;
373 if (succeededEdits
+ failedEdits
!== scriptFiles
.length
)
377 logLiveEditError
.call(this, liveEditError
, liveEditErrorData
, contextScript
);
381 * @param {?string} error
382 * @param {!DebuggerAgent.SetScriptSourceError=} errorData
383 * @param {!WebInspector.Script=} contextScript
384 * @this {WebInspector.JavaScriptSourceFrame}
386 function logLiveEditError(error
, errorData
, contextScript
)
388 var warningLevel
= WebInspector
.Console
.MessageLevel
.Warning
;
391 WebInspector
.console
.addMessage(WebInspector
.UIString("LiveEdit failed: %s", error
), warningLevel
);
394 var compileError
= errorData
.compileError
;
396 var messageText
= WebInspector
.UIString("LiveEdit compile failed: %s", compileError
.message
);
397 var message
= new WebInspector
.SourceFrameMessage(messageText
, WebInspector
.SourceFrameMessage
.Level
.Error
, compileError
.lineNumber
- 1, compileError
.columnNumber
+ 1);
398 this.addMessageToSource(message
);
400 WebInspector
.console
.addMessage(WebInspector
.UIString("Unknown LiveEdit error: %s; %s", JSON
.stringify(errorData
), error
), warningLevel
);
404 this._scriptsPanel
.setIgnoreExecutionLineEvents(true);
405 this._hasCommittedLiveEdit
= true;
406 var scriptFiles
= this._scriptFileForTarget
.valuesArray();
407 for (var i
= 0; i
< scriptFiles
.length
; ++i
)
408 scriptFiles
[i
].commitLiveEdit(liveEditCallback
.bind(this));
411 _didMergeToVM: function()
413 if (this._supportsEnabledBreakpointsWhileEditing())
415 this._updateDivergedInfobar();
416 this._restoreBreakpointsIfConsistentScripts();
419 _didDivergeFromVM: function()
421 if (this._supportsEnabledBreakpointsWhileEditing())
423 this._updateDivergedInfobar();
424 this._muteBreakpointsWhileEditing();
427 _muteBreakpointsWhileEditing: function()
431 for (var lineNumber
= 0; lineNumber
< this._textEditor
.linesCount
; ++lineNumber
) {
432 var breakpointDecoration
= this._textEditor
.getAttribute(lineNumber
, "breakpoint");
433 if (!breakpointDecoration
)
435 this._removeBreakpointDecoration(lineNumber
);
436 this._addBreakpointDecoration(lineNumber
, breakpointDecoration
.columnNumber
, breakpointDecoration
.condition
, breakpointDecoration
.enabled
, true);
441 _updateDivergedInfobar: function()
443 if (this._uiSourceCode
.project().type() !== WebInspector
.projectTypes
.FileSystem
) {
444 this._hideDivergedInfobar();
448 var scriptFiles
= this._scriptFileForTarget
.valuesArray();
449 var hasDivergedScript
= false;
450 for (var i
= 0; i
< scriptFiles
.length
; ++i
)
451 hasDivergedScript
= hasDivergedScript
|| scriptFiles
[i
].hasDivergedFromVM();
453 if (this._divergedInfobar
) {
454 if (!hasDivergedScript
|| this._hasCommittedLiveEdit
)
455 this._hideDivergedInfobar();
457 if (hasDivergedScript
&& !this._uiSourceCode
.isDirty() && !this._hasCommittedLiveEdit
)
458 this._showDivergedInfobar();
462 _supportsEnabledBreakpointsWhileEditing: function()
464 return this._uiSourceCode
.project().type() === WebInspector
.projectTypes
.Snippets
;
467 _restoreBreakpointsIfConsistentScripts: function()
469 var scriptFiles
= this._scriptFileForTarget
.valuesArray();
470 for (var i
= 0; i
< scriptFiles
.length
; ++i
)
471 if (scriptFiles
[i
].hasDivergedFromVM() || scriptFiles
[i
].isMergingToVM())
474 this._restoreBreakpointsAfterEditing();
477 _restoreBreakpointsAfterEditing: function()
480 var breakpoints
= {};
481 // Save and remove muted breakpoint decorations.
482 for (var lineNumber
= 0; lineNumber
< this._textEditor
.linesCount
; ++lineNumber
) {
483 var breakpointDecoration
= this._textEditor
.getAttribute(lineNumber
, "breakpoint");
484 if (breakpointDecoration
) {
485 breakpoints
[lineNumber
] = breakpointDecoration
;
486 this._removeBreakpointDecoration(lineNumber
);
490 // Remove all breakpoints.
491 this._removeAllBreakpoints();
493 // Restore all breakpoints from saved decorations.
494 for (var lineNumberString
in breakpoints
) {
495 var lineNumber
= parseInt(lineNumberString
, 10);
496 if (isNaN(lineNumber
))
498 var breakpointDecoration
= breakpoints
[lineNumberString
];
499 this._setBreakpoint(lineNumber
, breakpointDecoration
.columnNumber
, breakpointDecoration
.condition
, breakpointDecoration
.enabled
);
503 _removeAllBreakpoints: function()
505 var breakpoints
= this._breakpointManager
.breakpointsForUISourceCode(this._uiSourceCode
);
506 for (var i
= 0; i
< breakpoints
.length
; ++i
)
507 breakpoints
[i
].remove();
511 * @param {string} tokenType
514 _isIdentifier: function(tokenType
)
516 return tokenType
.startsWith("js-variable") || tokenType
.startsWith("js-property") || tokenType
== "js-def";
519 _getPopoverAnchor: function(element
, event
)
521 var target
= WebInspector
.context
.flavor(WebInspector
.Target
);
522 var debuggerModel
= WebInspector
.DebuggerModel
.fromTarget(target
);
523 if (!debuggerModel
|| !debuggerModel
.isPaused())
526 var textPosition
= this.textEditor
.coordinatesToCursorPosition(event
.x
, event
.y
);
529 var mouseLine
= textPosition
.startLine
;
530 var mouseColumn
= textPosition
.startColumn
;
531 var textSelection
= this.textEditor
.selection().normalize();
532 if (textSelection
&& !textSelection
.isEmpty()) {
533 if (textSelection
.startLine
!== textSelection
.endLine
|| textSelection
.startLine
!== mouseLine
|| mouseColumn
< textSelection
.startColumn
|| mouseColumn
> textSelection
.endColumn
)
536 var leftCorner
= this.textEditor
.cursorPositionToCoordinates(textSelection
.startLine
, textSelection
.startColumn
);
537 var rightCorner
= this.textEditor
.cursorPositionToCoordinates(textSelection
.endLine
, textSelection
.endColumn
);
538 var anchorBox
= new AnchorBox(leftCorner
.x
, leftCorner
.y
, rightCorner
.x
- leftCorner
.x
, leftCorner
.height
);
539 anchorBox
.highlight
= {
540 lineNumber
: textSelection
.startLine
,
541 startColumn
: textSelection
.startColumn
,
542 endColumn
: textSelection
.endColumn
- 1
544 anchorBox
.forSelection
= true;
548 var token
= this.textEditor
.tokenAtTextPosition(textPosition
.startLine
, textPosition
.startColumn
);
549 if (!token
|| !token
.type
)
551 var lineNumber
= textPosition
.startLine
;
552 var line
= this.textEditor
.line(lineNumber
);
553 var tokenContent
= line
.substring(token
.startColumn
, token
.endColumn
);
555 var isIdentifier
= this._isIdentifier(token
.type
);
556 if (!isIdentifier
&& (token
.type
!== "js-keyword" || tokenContent
!== "this"))
559 var leftCorner
= this.textEditor
.cursorPositionToCoordinates(lineNumber
, token
.startColumn
);
560 var rightCorner
= this.textEditor
.cursorPositionToCoordinates(lineNumber
, token
.endColumn
- 1);
561 var anchorBox
= new AnchorBox(leftCorner
.x
, leftCorner
.y
, rightCorner
.x
- leftCorner
.x
, leftCorner
.height
);
563 anchorBox
.highlight
= {
564 lineNumber
: lineNumber
,
565 startColumn
: token
.startColumn
,
566 endColumn
: token
.endColumn
- 1
572 _resolveObjectForPopover: function(anchorBox
, showCallback
, objectGroupName
)
574 var target
= WebInspector
.context
.flavor(WebInspector
.Target
);
575 var debuggerModel
= WebInspector
.DebuggerModel
.fromTarget(target
);
576 if (!debuggerModel
|| !debuggerModel
.isPaused()) {
577 this._popoverHelper
.hidePopover();
580 var lineNumber
= anchorBox
.highlight
.lineNumber
;
581 var startHighlight
= anchorBox
.highlight
.startColumn
;
582 var endHighlight
= anchorBox
.highlight
.endColumn
;
583 var line
= this.textEditor
.line(lineNumber
);
584 if (!anchorBox
.forSelection
) {
585 while (startHighlight
> 1 && line
.charAt(startHighlight
- 1) === '.') {
586 var token
= this.textEditor
.tokenAtTextPosition(lineNumber
, startHighlight
- 2);
587 if (!token
|| !token
.type
) {
588 this._popoverHelper
.hidePopover();
591 startHighlight
= token
.startColumn
;
594 var evaluationText
= line
.substring(startHighlight
, endHighlight
+ 1);
595 var selectedCallFrame
= debuggerModel
.selectedCallFrame();
596 selectedCallFrame
.evaluate(evaluationText
, objectGroupName
, false, true, false, false, showObjectPopover
.bind(this));
599 * @param {?RuntimeAgent.RemoteObject} result
600 * @param {boolean=} wasThrown
601 * @this {WebInspector.JavaScriptSourceFrame}
603 function showObjectPopover(result
, wasThrown
)
605 var target
= WebInspector
.context
.flavor(WebInspector
.Target
);
606 if (selectedCallFrame
.target() != target
|| !debuggerModel
.isPaused() || !result
) {
607 this._popoverHelper
.hidePopover();
610 this._popoverAnchorBox
= anchorBox
;
611 showCallback(target
.runtimeModel
.createRemoteObject(result
), wasThrown
, this._popoverAnchorBox
);
612 // Popover may have been removed by showCallback().
613 if (this._popoverAnchorBox
) {
614 var highlightRange
= new WebInspector
.TextRange(lineNumber
, startHighlight
, lineNumber
, endHighlight
);
615 this._popoverAnchorBox
._highlightDescriptor
= this.textEditor
.highlightRange(highlightRange
, "source-frame-eval-expression");
620 _onHidePopover: function()
622 if (!this._popoverAnchorBox
)
624 if (this._popoverAnchorBox
._highlightDescriptor
)
625 this.textEditor
.removeHighlight(this._popoverAnchorBox
._highlightDescriptor
);
626 delete this._popoverAnchorBox
;
630 * @param {number} lineNumber
631 * @param {number} columnNumber
632 * @param {string} condition
633 * @param {boolean} enabled
634 * @param {boolean} mutedWhileEditing
636 _addBreakpointDecoration: function(lineNumber
, columnNumber
, condition
, enabled
, mutedWhileEditing
)
639 condition
: condition
,
641 columnNumber
: columnNumber
644 this.textEditor
.setAttribute(lineNumber
, "breakpoint", breakpoint
);
646 var disabled
= !enabled
|| mutedWhileEditing
;
647 this.textEditor
.addBreakpoint(lineNumber
, disabled
, !!condition
);
650 _removeBreakpointDecoration: function(lineNumber
)
652 this.textEditor
.removeAttribute(lineNumber
, "breakpoint");
653 this.textEditor
.removeBreakpoint(lineNumber
);
656 _onKeyDown: function(event
)
658 if (event
.keyIdentifier
=== "U+001B") { // Escape key
659 if (this._popoverHelper
.isPopoverVisible()) {
660 this._popoverHelper
.hidePopover();
667 * @param {number} lineNumber
668 * @param {!WebInspector.BreakpointManager.Breakpoint=} breakpoint
670 _editBreakpointCondition: function(lineNumber
, breakpoint
)
672 this._conditionElement
= this._createConditionElement(lineNumber
);
673 this.textEditor
.addDecoration(lineNumber
, this._conditionElement
);
676 * @this {WebInspector.JavaScriptSourceFrame}
678 function finishEditing(committed
, element
, newText
)
680 this.textEditor
.removeDecoration(lineNumber
, this._conditionElement
);
681 delete this._conditionEditorElement
;
682 delete this._conditionElement
;
687 breakpoint
.setCondition(newText
);
689 this._createNewBreakpoint(lineNumber
, 0, newText
, true);
692 var config
= new WebInspector
.InplaceEditor
.Config(finishEditing
.bind(this, true), finishEditing
.bind(this, false));
693 WebInspector
.InplaceEditor
.startEditing(this._conditionEditorElement
, config
);
694 this._conditionEditorElement
.value
= breakpoint
? breakpoint
.condition() : "";
695 this._conditionEditorElement
.select();
698 _createConditionElement: function(lineNumber
)
700 var conditionElement
= createElementWithClass("div", "source-frame-breakpoint-condition");
702 var labelElement
= conditionElement
.createChild("label", "source-frame-breakpoint-message");
703 labelElement
.htmlFor
= "source-frame-breakpoint-condition";
704 labelElement
.createTextChild(WebInspector
.UIString("The breakpoint on line %d will stop only if this expression is true:", lineNumber
+ 1));
706 var editorElement
= conditionElement
.createChild("input", "monospace");
707 editorElement
.id
= "source-frame-breakpoint-condition";
708 editorElement
.type
= "text";
709 this._conditionEditorElement
= editorElement
;
711 return conditionElement
;
715 * @param {!WebInspector.UILocation} uiLocation
717 setExecutionLocation: function(uiLocation
)
719 this._executionLocation
= uiLocation
;
723 this.textEditor
.setExecutionLocation(uiLocation
.lineNumber
, uiLocation
.columnNumber
);
724 if (this.isShowing()) {
725 // We need CodeMirrorTextEditor to be initialized prior to this call. @see crbug.com/506566
726 setImmediate(this._generateValuesInSource
.bind(this));
730 _generateValuesInSource: function()
732 if (!WebInspector
.moduleSetting("inlineVariableValues").get())
734 var executionContext
= WebInspector
.context
.flavor(WebInspector
.ExecutionContext
);
735 if (!executionContext
)
737 var callFrame
= executionContext
.debuggerModel
.selectedCallFrame();
741 var localScope
= callFrame
.localScope();
742 var functionLocation
= callFrame
.functionLocation();
743 if (localScope
&& functionLocation
)
744 localScope
.object().getAllProperties(false, this._prepareScopeVariables
.bind(this, callFrame
));
746 if (this._clearValueWidgetsTimer
) {
747 clearTimeout(this._clearValueWidgetsTimer
);
748 delete this._clearValueWidgetsTimer
;
753 * @param {!WebInspector.DebuggerModel.CallFrame} callFrame
754 * @param {?Array.<!WebInspector.RemoteObjectProperty>} properties
755 * @param {?Array.<!WebInspector.RemoteObjectProperty>} internalProperties
757 _prepareScopeVariables: function(callFrame
, properties
, internalProperties
)
759 if (!properties
|| !properties
.length
|| properties
.length
> 500) {
760 this._clearValueWidgets();
764 var functionUILocation
= WebInspector
.debuggerWorkspaceBinding
.rawLocationToUILocation(/**@type {!WebInspector.DebuggerModel.Location} */ (callFrame
.functionLocation()));
765 var executionUILocation
= WebInspector
.debuggerWorkspaceBinding
.rawLocationToUILocation(callFrame
.location());
766 if (functionUILocation
.uiSourceCode
!== this._uiSourceCode
|| executionUILocation
.uiSourceCode
!== this._uiSourceCode
) {
767 this._clearValueWidgets();
771 var fromLine
= functionUILocation
.lineNumber
;
772 var fromColumn
= functionUILocation
.columnNumber
;
773 var toLine
= executionUILocation
.lineNumber
;
775 // Make sure we have a chance to update all existing widgets.
776 if (this._valueWidgets
) {
777 for (var line
of this._valueWidgets
.keys())
778 toLine
= Math
.max(toLine
, line
+ 1);
780 if (fromLine
>= toLine
|| toLine
- fromLine
> 500) {
781 this._clearValueWidgets();
785 var valuesMap
= new Map();
786 for (var property
of properties
)
787 valuesMap
.set(property
.name
, property
.value
);
789 /** @type {!Map.<number, !Set<string>>} */
790 var namesPerLine
= new Map();
791 var tokenizer
= new WebInspector
.CodeMirrorUtils
.TokenizerFactory().createTokenizer("text/javascript");
792 tokenizer(this.textEditor
.line(fromLine
).substring(fromColumn
), processToken
.bind(this, fromLine
));
793 for (var i
= fromLine
+ 1; i
< toLine
; ++i
)
794 tokenizer(this.textEditor
.line(i
), processToken
.bind(this, i
));
797 * @param {number} lineNumber
798 * @param {string} tokenValue
799 * @param {?string} tokenType
800 * @param {number} column
801 * @param {number} newColumn
802 * @this {WebInspector.JavaScriptSourceFrame}
804 function processToken(lineNumber
, tokenValue
, tokenType
, column
, newColumn
)
806 if (tokenType
&& this._isIdentifier(tokenType
) && valuesMap
.get(tokenValue
)) {
807 var names
= namesPerLine
.get(lineNumber
);
810 namesPerLine
.set(lineNumber
, names
);
812 names
.add(tokenValue
);
815 this.textEditor
.operation(this._renderDecorations
.bind(this, valuesMap
, namesPerLine
, fromLine
, toLine
));
819 * @param {!Map.<string,!WebInspector.RemoteObject>} valuesMap
820 * @param {!Map.<number, !Set<string>>} namesPerLine
821 * @param {number} fromLine
822 * @param {number} toLine
824 _renderDecorations: function(valuesMap
, namesPerLine
, fromLine
, toLine
)
826 var formatter
= new WebInspector
.RemoteObjectPreviewFormatter();
827 for (var i
= fromLine
; i
< toLine
; ++i
) {
828 var names
= namesPerLine
.get(i
);
829 var oldWidget
= this._valueWidgets
.get(i
);
832 this._valueWidgets
.delete(i
);
833 this.textEditor
.removeDecoration(i
, oldWidget
);
838 var widget
= createElementWithClass("div", "text-editor-value-decoration");
839 var base
= this.textEditor
.cursorPositionToCoordinates(i
, 0);
840 var offset
= this.textEditor
.cursorPositionToCoordinates(i
, this.textEditor
.line(i
).length
);
841 var codeMirrorLinesLeftPadding
= 4;
842 var left
= offset
.x
- base
.x
+ codeMirrorLinesLeftPadding
;
843 widget
.style
.left
= left
+ "px";
844 widget
.__nameToToken
= new Map();
845 widget
.__lineNumber
= i
;
847 var renderedNameCount
= 0;
848 for (var name
of names
) {
849 if (renderedNameCount
> 10)
851 if (namesPerLine
.get(i
- 1) && namesPerLine
.get(i
- 1).has(name
))
852 continue; // Only render name once in the given continuous block.
853 if (renderedNameCount
)
854 widget
.createTextChild(", ");
855 var nameValuePair
= widget
.createChild("span");
856 widget
.__nameToToken
.set(name
, nameValuePair
);
857 nameValuePair
.createTextChild(name
+ " = ");
858 var value
= valuesMap
.get(name
);
859 var propertyCount
= value
.preview
? value
.preview
.properties
.length
: 0;
860 var entryCount
= value
.preview
&& value
.preview
.entries
? value
.preview
.entries
.length
: 0;
861 if (value
.preview
&& propertyCount
+ entryCount
< 10)
862 formatter
.appendObjectPreview(nameValuePair
, value
.preview
);
864 nameValuePair
.appendChild(WebInspector
.ObjectPropertiesSection
.createValueElement(value
, false));
868 var widgetChanged
= true;
870 widgetChanged
= false;
871 for (var name
of widget
.__nameToToken
.keys()) {
872 var oldText
= oldWidget
.__nameToToken
.get(name
) ? oldWidget
.__nameToToken
.get(name
).textContent
: "";
873 var newText
= widget
.__nameToToken
.get(name
) ? widget
.__nameToToken
.get(name
).textContent
: "";
874 if (newText
!== oldText
) {
875 widgetChanged
= true;
876 // value has changed, update it.
877 WebInspector
.runCSSAnimationOnce(/** @type {!Element} */ (widget
.__nameToToken
.get(name
)), "source-frame-value-update-highlight");
881 this._valueWidgets
.delete(i
);
882 this.textEditor
.removeDecoration(i
, oldWidget
);
886 this._valueWidgets
.set(i
, widget
);
887 this.textEditor
.addDecoration(i
, widget
);
892 clearExecutionLine: function()
894 if (this.loaded
&& this._executionLocation
)
895 this.textEditor
.clearExecutionLine();
896 delete this._executionLocation
;
897 this._clearValueWidgetsTimer
= setTimeout(this._clearValueWidgets
.bind(this), 1000);
900 _clearValueWidgets: function()
902 delete this._clearValueWidgetsTimer
;
903 for (var line
of this._valueWidgets
.keys())
904 this.textEditor
.removeDecoration(line
, this._valueWidgets
.get(line
));
905 this._valueWidgets
.clear();
911 _shouldIgnoreExternalBreakpointEvents: function()
913 if (this._supportsEnabledBreakpointsWhileEditing())
917 var scriptFiles
= this._scriptFileForTarget
.valuesArray();
918 for (var i
= 0; i
< scriptFiles
.length
; ++i
) {
919 if (scriptFiles
[i
].isDivergingFromVM() || scriptFiles
[i
].isMergingToVM())
925 _breakpointAdded: function(event
)
927 var uiLocation
= /** @type {!WebInspector.UILocation} */ (event
.data
.uiLocation
);
928 if (uiLocation
.uiSourceCode
!== this._uiSourceCode
)
930 if (this._shouldIgnoreExternalBreakpointEvents())
933 var breakpoint
= /** @type {!WebInspector.BreakpointManager.Breakpoint} */ (event
.data
.breakpoint
);
935 this._addBreakpointDecoration(uiLocation
.lineNumber
, uiLocation
.columnNumber
, breakpoint
.condition(), breakpoint
.enabled(), false);
938 _breakpointRemoved: function(event
)
940 var uiLocation
= /** @type {!WebInspector.UILocation} */ (event
.data
.uiLocation
);
941 if (uiLocation
.uiSourceCode
!== this._uiSourceCode
)
943 if (this._shouldIgnoreExternalBreakpointEvents())
946 var remainingBreakpoint
= this._breakpointManager
.findBreakpointOnLine(this._uiSourceCode
, uiLocation
.lineNumber
);
947 if (!remainingBreakpoint
&& this.loaded
)
948 this._removeBreakpointDecoration(uiLocation
.lineNumber
);
951 _consoleMessageAdded: function(event
)
953 var message
= /** @type {!WebInspector.PresentationConsoleMessage} */ (event
.data
);
955 this.addMessageToSource(this._sourceFrameMessage(message
));
958 _consoleMessageRemoved: function(event
)
960 var message
= /** @type {!WebInspector.PresentationConsoleMessage} */ (event
.data
);
962 this.removeMessageFromSource(this._sourceFrameMessage(message
));
966 * @param {!WebInspector.PresentationConsoleMessage} message
967 * @return {!WebInspector.SourceFrameMessage}
969 _sourceFrameMessage: function(message
)
971 return WebInspector
.SourceFrameMessage
.fromConsoleMessage(message
.originalMessage
, message
.lineNumber(), message
.columnNumber());
974 _consoleMessagesCleared: function(event
)
976 this.clearMessages();
980 * @param {!WebInspector.Event} event
982 _onSourceMappingChanged: function(event
)
984 var data
= /** @type {{target: !WebInspector.Target}} */ (event
.data
);
985 this._updateScriptFile(data
.target
);
986 this._updateLinesWithoutMappingHighlight();
989 _updateLinesWithoutMappingHighlight: function()
991 var linesCount
= this.textEditor
.linesCount
;
992 for (var i
= 0; i
< linesCount
; ++i
) {
993 var lineHasMapping
= WebInspector
.debuggerWorkspaceBinding
.uiLineHasMapping(this._uiSourceCode
, i
);
995 this._hasLineWithoutMapping
= true;
996 if (this._hasLineWithoutMapping
)
997 this.textEditor
.toggleLineClass(i
, "cm-line-without-source-mapping", !lineHasMapping
);
1002 * @param {!WebInspector.Target} target
1004 _updateScriptFile: function(target
)
1006 var oldScriptFile
= this._scriptFileForTarget
.get(target
);
1007 var newScriptFile
= WebInspector
.debuggerWorkspaceBinding
.scriptFile(this._uiSourceCode
, target
);
1008 this._scriptFileForTarget
.remove(target
);
1009 if (oldScriptFile
) {
1010 oldScriptFile
.removeEventListener(WebInspector
.ResourceScriptFile
.Events
.DidMergeToVM
, this._didMergeToVM
, this);
1011 oldScriptFile
.removeEventListener(WebInspector
.ResourceScriptFile
.Events
.DidDivergeFromVM
, this._didDivergeFromVM
, this);
1012 if (this._muted
&& !this._uiSourceCode
.isDirty())
1013 this._restoreBreakpointsIfConsistentScripts();
1016 this._scriptFileForTarget
.set(target
, newScriptFile
);
1018 delete this._hasCommittedLiveEdit
;
1019 this._updateDivergedInfobar();
1021 if (newScriptFile
) {
1022 newScriptFile
.addEventListener(WebInspector
.ResourceScriptFile
.Events
.DidMergeToVM
, this._didMergeToVM
, this);
1023 newScriptFile
.addEventListener(WebInspector
.ResourceScriptFile
.Events
.DidDivergeFromVM
, this._didDivergeFromVM
, this);
1025 newScriptFile
.checkMapping();
1029 onTextEditorContentLoaded: function()
1031 if (this._executionLocation
)
1032 this.setExecutionLocation(this._executionLocation
);
1034 var breakpointLocations
= this._breakpointManager
.breakpointLocationsForUISourceCode(this._uiSourceCode
);
1035 for (var i
= 0; i
< breakpointLocations
.length
; ++i
)
1036 this._breakpointAdded({data
:breakpointLocations
[i
]});
1038 var messages
= WebInspector
.presentationConsoleMessageHelper
.consoleMessages(this._uiSourceCode
);
1039 for (var message
of messages
)
1040 this.addMessageToSource(this._sourceFrameMessage(message
));
1042 var scriptFiles
= this._scriptFileForTarget
.valuesArray();
1043 for (var i
= 0; i
< scriptFiles
.length
; ++i
)
1044 scriptFiles
[i
].checkMapping();
1046 this._updateLinesWithoutMappingHighlight();
1050 * @param {!WebInspector.Event} event
1052 _handleGutterClick: function(event
)
1057 var eventData
= /** @type {!WebInspector.CodeMirrorTextEditor.GutterClickEventData} */ (event
.data
);
1058 var lineNumber
= eventData
.lineNumber
;
1059 var eventObject
= eventData
.event
;
1061 if (eventObject
.button
!= 0 || eventObject
.altKey
|| eventObject
.ctrlKey
|| eventObject
.metaKey
)
1064 this._toggleBreakpoint(lineNumber
, eventObject
.shiftKey
);
1065 eventObject
.consume(true);
1069 * @param {number} lineNumber
1070 * @param {boolean} onlyDisable
1072 _toggleBreakpoint: function(lineNumber
, onlyDisable
)
1074 var breakpoint
= this._breakpointManager
.findBreakpointOnLine(this._uiSourceCode
, lineNumber
);
1077 breakpoint
.setEnabled(!breakpoint
.enabled());
1079 breakpoint
.remove();
1081 this._createNewBreakpoint(lineNumber
, 0, "", true);
1085 * @param {number} lineNumber
1086 * @param {number} columnNumber
1087 * @param {string} condition
1088 * @param {boolean} enabled
1090 _createNewBreakpoint: function(lineNumber
, columnNumber
, condition
, enabled
)
1092 this._setBreakpoint(lineNumber
, columnNumber
, condition
, enabled
);
1093 WebInspector
.userMetrics
.ScriptsBreakpointSet
.record();
1096 toggleBreakpointOnCurrentLine: function()
1101 var selection
= this.textEditor
.selection();
1104 this._toggleBreakpoint(selection
.startLine
, false);
1108 * @param {number} lineNumber
1109 * @param {number} columnNumber
1110 * @param {string} condition
1111 * @param {boolean} enabled
1113 _setBreakpoint: function(lineNumber
, columnNumber
, condition
, enabled
)
1115 this._breakpointManager
.setBreakpoint(this._uiSourceCode
, lineNumber
, columnNumber
, condition
, enabled
);
1120 this._breakpointManager
.removeEventListener(WebInspector
.BreakpointManager
.Events
.BreakpointAdded
, this._breakpointAdded
, this);
1121 this._breakpointManager
.removeEventListener(WebInspector
.BreakpointManager
.Events
.BreakpointRemoved
, this._breakpointRemoved
, this);
1122 WebInspector
.presentationConsoleMessageHelper
.removeConsoleMessageEventListener(WebInspector
.PresentationConsoleMessageHelper
.Events
.ConsoleMessageAdded
, this._uiSourceCode
, this._consoleMessageAdded
, this);
1123 WebInspector
.presentationConsoleMessageHelper
.removeConsoleMessageEventListener(WebInspector
.PresentationConsoleMessageHelper
.Events
.ConsoleMessageRemoved
, this._uiSourceCode
, this._consoleMessageRemoved
, this);
1124 WebInspector
.presentationConsoleMessageHelper
.removeConsoleMessageEventListener(WebInspector
.PresentationConsoleMessageHelper
.Events
.ConsoleMessagesCleared
, this._uiSourceCode
, this._consoleMessagesCleared
, this);
1125 this._uiSourceCode
.removeEventListener(WebInspector
.UISourceCode
.Events
.SourceMappingChanged
, this._onSourceMappingChanged
, this);
1126 this._uiSourceCode
.removeEventListener(WebInspector
.UISourceCode
.Events
.WorkingCopyChanged
, this._workingCopyChanged
, this);
1127 this._uiSourceCode
.removeEventListener(WebInspector
.UISourceCode
.Events
.WorkingCopyCommitted
, this._workingCopyCommitted
, this);
1128 this._uiSourceCode
.removeEventListener(WebInspector
.UISourceCode
.Events
.TitleChanged
, this._showBlackboxInfobarIfNeeded
, this);
1129 WebInspector
.moduleSetting("skipStackFramesPattern").removeChangeListener(this._showBlackboxInfobarIfNeeded
, this);
1130 WebInspector
.moduleSetting("skipContentScripts").removeChangeListener(this._showBlackboxInfobarIfNeeded
, this);
1131 WebInspector
.UISourceCodeFrame
.prototype.dispose
.call(this);
1134 __proto__
: WebInspector
.UISourceCodeFrame
.prototype