blob: fda3e0c973ad13017083ce5d1a409d0c6b0eaedd [file] [log] [blame]
/*
* Copyright (C) 2009 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
WebInspector.SourceFrame = function(delegate, url)
{
WebInspector.TextViewerDelegate.call(this);
this._delegate = delegate;
this._url = url;
this._textModel = new WebInspector.TextEditorModel();
this._textModel.replaceTabsWithSpaces = true;
this._textViewer = new WebInspector.TextViewer(this._textModel, WebInspector.platform, this._url, this);
this._textViewer.element.addStyleClass("script-view");
this._visible = false;
this._currentSearchResultIndex = -1;
this._searchResults = [];
this._messages = [];
this._rowMessages = {};
this._messageBubbles = {};
this._breakpoints = {};
}
WebInspector.SourceFrame.Events = {
Loaded: "loaded"
}
WebInspector.SourceFrame.prototype = {
get visible()
{
return this._textViewer.visible;
},
set visible(x)
{
this._textViewer.visible = x;
},
show: function(parentElement)
{
this._ensureContentLoaded();
this._textViewer.show(parentElement);
this._textViewer.resize();
if (this.loaded) {
if (this._scrollTop)
this._textViewer.scrollTop = this._scrollTop;
if (this._scrollLeft)
this._textViewer.scrollLeft = this._scrollLeft;
}
},
hide: function()
{
if (this.loaded) {
this._scrollTop = this._textViewer.scrollTop;
this._scrollLeft = this._textViewer.scrollLeft;
this._textViewer.freeCachedElements();
}
this._textViewer.hide();
this._hidePopup();
this._clearLineHighlight();
},
detach: function()
{
this._textViewer.detach();
},
get element()
{
return this._textViewer.element;
},
get loaded()
{
return !!this._content;
},
hasContent: function()
{
return true;
},
_ensureContentLoaded: function()
{
if (!this._contentRequested) {
this._contentRequested = true;
this.requestContent(this._initializeTextViewer.bind(this));
}
},
requestContent: function(callback)
{
this._delegate.requestContent(callback);
},
markDiff: function(diffData)
{
if (this._diffLines && this.loaded)
this._removeDiffDecorations();
this._diffLines = diffData;
if (this.loaded)
this._updateDiffDecorations();
},
addMessage: function(msg)
{
this._messages.push(msg);
if (this.loaded)
this.addMessageToSource(msg.line - 1, msg);
},
clearMessages: function()
{
for (var line in this._messageBubbles) {
var bubble = this._messageBubbles[line];
bubble.parentNode.removeChild(bubble);
}
this._messages = [];
this._rowMessages = {};
this._messageBubbles = {};
this._textViewer.resize();
},
get textModel()
{
return this._textModel;
},
get scrollTop()
{
return this.loaded ? this._textViewer.scrollTop : this._scrollTop;
},
set scrollTop(scrollTop)
{
this._scrollTop = scrollTop;
if (this.loaded)
this._textViewer.scrollTop = scrollTop;
},
highlightLine: function(line)
{
if (this.loaded)
this._textViewer.highlightLine(line);
else
this._lineToHighlight = line;
},
_clearLineHighlight: function()
{
if (this.loaded)
this._textViewer.clearLineHighlight();
else
delete this._lineToHighlight;
},
_saveViewerState: function()
{
this._viewerState = {
textModelContent: this._textModel.text,
executionLineNumber: this._executionLineNumber,
messages: this._messages,
diffLines: this._diffLines,
breakpoints: this._breakpoints
};
},
_restoreViewerState: function()
{
if (!this._viewerState)
return;
this._textModel.setText(null, this._viewerState.textModelContent);
this._messages = this._viewerState.messages;
this._diffLines = this._viewerState.diffLines;
this._setTextViewerDecorations();
if (typeof this._viewerState.executionLineNumber === "number") {
this.clearExecutionLine();
this.setExecutionLine(this._viewerState.executionLineNumber);
}
var oldBreakpoints = this._breakpoints;
this._breakpoints = {};
for (var lineNumber in oldBreakpoints)
this.removeBreakpoint(Number(lineNumber));
var newBreakpoints = this._viewerState.breakpoints;
for (var lineNumber in newBreakpoints) {
lineNumber = Number(lineNumber);
var breakpoint = newBreakpoints[lineNumber];
this.addBreakpoint(lineNumber, breakpoint.resolved, breakpoint.conditional, breakpoint.enabled);
}
delete this._viewerState;
},
isContentEditable: function()
{
return this._delegate.canEditScriptSource();
},
readOnlyStateChanged: function(readOnly)
{
WebInspector.markBeingEdited(this._textViewer.element, !readOnly);
},
startEditing: function()
{
if (!this._viewerState) {
this._saveViewerState();
this._delegate.setScriptSourceIsBeingEdited(true);
}
WebInspector.searchController.cancelSearch();
this.clearMessages();
},
endEditing: function(oldRange, newRange)
{
if (!oldRange || !newRange)
return;
// Adjust execution line number.
if (typeof this._executionLineNumber === "number") {
var newExecutionLineNumber = this._lineNumberAfterEditing(this._executionLineNumber, oldRange, newRange);
this.clearExecutionLine();
this.setExecutionLine(newExecutionLineNumber, true);
}
// Adjust breakpoints.
var oldBreakpoints = this._breakpoints;
this._breakpoints = {};
for (var lineNumber in oldBreakpoints) {
lineNumber = Number(lineNumber);
var breakpoint = oldBreakpoints[lineNumber];
var newLineNumber = this._lineNumberAfterEditing(lineNumber, oldRange, newRange);
if (lineNumber === newLineNumber)
this._breakpoints[lineNumber] = breakpoint;
else {
this.removeBreakpoint(lineNumber);
this.addBreakpoint(newLineNumber, breakpoint.resolved, breakpoint.conditional, breakpoint.enabled);
}
}
},
_lineNumberAfterEditing: function(lineNumber, oldRange, newRange)
{
var shiftOffset = lineNumber <= oldRange.startLine ? 0 : newRange.linesCount - oldRange.linesCount;
// Special case of editing the line itself. We should decide whether the line number should move below or not.
if (lineNumber === oldRange.startLine) {
var whiteSpacesRegex = /^[\s\xA0]*$/;
for (var i = 0; lineNumber + i <= newRange.endLine; ++i) {
if (!whiteSpacesRegex.test(this._textModel.line(lineNumber + i))) {
shiftOffset = i;
break;
}
}
}
var newLineNumber = Math.max(0, lineNumber + shiftOffset);
if (oldRange.startLine < lineNumber && lineNumber < oldRange.endLine)
newLineNumber = oldRange.startLine;
return newLineNumber;
},
_initializeTextViewer: function(mimeType, content)
{
this._textViewer.mimeType = mimeType;
this._content = content;
this._textModel.setText(null, content);
var element = this._textViewer.element;
if (this._delegate.debuggingSupported()) {
element.addEventListener("contextmenu", this._contextMenu.bind(this), true);
element.addEventListener("mousedown", this._mouseDown.bind(this), true);
element.addEventListener("mousemove", this._mouseMove.bind(this), true);
element.addEventListener("scroll", this._scroll.bind(this), true);
}
this._textViewer.beginUpdates();
this._setTextViewerDecorations();
if (typeof this._executionLineNumber === "number")
this.setExecutionLine(this._executionLineNumber);
if (this._lineToHighlight) {
this.highlightLine(this._lineToHighlight);
delete this._lineToHighlight;
}
if (this._delayedFindSearchMatches) {
this._delayedFindSearchMatches();
delete this._delayedFindSearchMatches;
}
this.dispatchEventToListeners(WebInspector.SourceFrame.Events.Loaded);
this._textViewer.endUpdates();
if (this._parentElement)
this.show(this._parentElement)
},
_setTextViewerDecorations: function()
{
this._rowMessages = {};
this._messageBubbles = {};
this._textViewer.beginUpdates();
this._addExistingMessagesToSource();
this._updateDiffDecorations();
this._textViewer.resize();
this._textViewer.endUpdates();
},
performSearch: function(query, callback)
{
// Call searchCanceled since it will reset everything we need before doing a new search.
this.searchCanceled();
function doFindSearchMatches(query)
{
this._currentSearchResultIndex = -1;
this._searchResults = [];
// First do case-insensitive search.
var regexObject = createSearchRegex(query);
this._collectRegexMatches(regexObject, this._searchResults);
// Then try regex search if user knows the / / hint.
try {
if (/^\/.*\/$/.test(query))
this._collectRegexMatches(new RegExp(query.substring(1, query.length - 1)), this._searchResults);
} catch (e) {
// Silent catch.
}
callback(this, this._searchResults.length);
}
if (this.loaded)
doFindSearchMatches.call(this, query);
else
this._delayedFindSearchMatches = doFindSearchMatches.bind(this, query);
this._ensureContentLoaded();
},
searchCanceled: function()
{
delete this._delayedFindSearchMatches;
if (!this.loaded)
return;
this._currentSearchResultIndex = -1;
this._searchResults = [];
this._textViewer.markAndRevealRange(null);
},
jumpToFirstSearchResult: function()
{
this._jumpToSearchResult(0);
},
jumpToLastSearchResult: function()
{
this._jumpToSearchResult(this._searchResults.length - 1);
},
jumpToNextSearchResult: function()
{
this._jumpToSearchResult(this._currentSearchResultIndex + 1);
},
jumpToPreviousSearchResult: function()
{
this._jumpToSearchResult(this._currentSearchResultIndex - 1);
},
showingFirstSearchResult: function()
{
return this._searchResults.length && this._currentSearchResultIndex === 0;
},
showingLastSearchResult: function()
{
return this._searchResults.length && this._currentSearchResultIndex === (this._searchResults.length - 1);
},
_jumpToSearchResult: function(index)
{
if (!this.loaded || !this._searchResults.length)
return;
this._currentSearchResultIndex = (index + this._searchResults.length) % this._searchResults.length;
this._textViewer.markAndRevealRange(this._searchResults[this._currentSearchResultIndex]);
},
_collectRegexMatches: function(regexObject, ranges)
{
for (var i = 0; i < this._textModel.linesCount; ++i) {
var line = this._textModel.line(i);
var offset = 0;
do {
var match = regexObject.exec(line);
if (match) {
ranges.push(new WebInspector.TextRange(i, offset + match.index, i, offset + match.index + match[0].length));
offset += match.index + 1;
line = line.substring(match.index + 1);
}
} while (match)
}
return ranges;
},
_incrementMessageRepeatCount: function(msg, repeatDelta)
{
if (!msg._resourceMessageLineElement)
return;
if (!msg._resourceMessageRepeatCountElement) {
var repeatedElement = document.createElement("span");
msg._resourceMessageLineElement.appendChild(repeatedElement);
msg._resourceMessageRepeatCountElement = repeatedElement;
}
msg.repeatCount += repeatDelta;
msg._resourceMessageRepeatCountElement.textContent = WebInspector.UIString(" (repeated %d times)", msg.repeatCount);
},
setExecutionLine: function(lineNumber, skipRevealLine)
{
this._executionLineNumber = lineNumber;
if (this.loaded) {
this._textViewer.addDecoration(lineNumber, "webkit-execution-line");
if (!skipRevealLine)
this._textViewer.revealLine(lineNumber);
}
},
clearExecutionLine: function()
{
if (this.loaded)
this._textViewer.removeDecoration(this._executionLineNumber, "webkit-execution-line");
delete this._executionLineNumber;
},
_updateDiffDecorations: function()
{
if (!this._diffLines)
return;
function addDecorations(textViewer, lines, className)
{
for (var i = 0; i < lines.length; ++i)
textViewer.addDecoration(lines[i], className);
}
addDecorations(this._textViewer, this._diffLines.added, "webkit-added-line");
addDecorations(this._textViewer, this._diffLines.removed, "webkit-removed-line");
addDecorations(this._textViewer, this._diffLines.changed, "webkit-changed-line");
},
_removeDiffDecorations: function()
{
function removeDecorations(textViewer, lines, className)
{
for (var i = 0; i < lines.length; ++i)
textViewer.removeDecoration(lines[i], className);
}
removeDecorations(this._textViewer, this._diffLines.added, "webkit-added-line");
removeDecorations(this._textViewer, this._diffLines.removed, "webkit-removed-line");
removeDecorations(this._textViewer, this._diffLines.changed, "webkit-changed-line");
},
_addExistingMessagesToSource: function()
{
var length = this._messages.length;
for (var i = 0; i < length; ++i)
this.addMessageToSource(this._messages[i].line - 1, this._messages[i]);
},
addMessageToSource: function(lineNumber, msg)
{
if (lineNumber >= this._textModel.linesCount)
lineNumber = this._textModel.linesCount - 1;
if (lineNumber < 0)
lineNumber = 0;
var messageBubbleElement = this._messageBubbles[lineNumber];
if (!messageBubbleElement || messageBubbleElement.nodeType !== Node.ELEMENT_NODE || !messageBubbleElement.hasStyleClass("webkit-html-message-bubble")) {
messageBubbleElement = document.createElement("div");
messageBubbleElement.className = "webkit-html-message-bubble";
this._messageBubbles[lineNumber] = messageBubbleElement;
this._textViewer.addDecoration(lineNumber, messageBubbleElement);
}
var rowMessages = this._rowMessages[lineNumber];
if (!rowMessages) {
rowMessages = [];
this._rowMessages[lineNumber] = rowMessages;
}
for (var i = 0; i < rowMessages.length; ++i) {
if (rowMessages[i].isEqual(msg)) {
this._incrementMessageRepeatCount(rowMessages[i], msg.repeatDelta);
return;
}
}
rowMessages.push(msg);
var imageURL;
switch (msg.level) {
case WebInspector.ConsoleMessage.MessageLevel.Error:
messageBubbleElement.addStyleClass("webkit-html-error-message");
imageURL = "Images/errorIcon.png";
break;
case WebInspector.ConsoleMessage.MessageLevel.Warning:
messageBubbleElement.addStyleClass("webkit-html-warning-message");
imageURL = "Images/warningIcon.png";
break;
}
var messageLineElement = document.createElement("div");
messageLineElement.className = "webkit-html-message-line";
messageBubbleElement.appendChild(messageLineElement);
// Create the image element in the Inspector's document so we can use relative image URLs.
var image = document.createElement("img");
image.src = imageURL;
image.className = "webkit-html-message-icon";
messageLineElement.appendChild(image);
messageLineElement.appendChild(document.createTextNode(msg.message));
msg._resourceMessageLineElement = messageLineElement;
},
addBreakpoint: function(lineNumber, resolved, conditional, enabled)
{
this._breakpoints[lineNumber] = {
resolved: resolved,
conditional: conditional,
enabled: enabled
};
this._textViewer.beginUpdates();
this._textViewer.addDecoration(lineNumber, "webkit-breakpoint");
if (!enabled)
this._textViewer.addDecoration(lineNumber, "webkit-breakpoint-disabled");
if (conditional)
this._textViewer.addDecoration(lineNumber, "webkit-breakpoint-conditional");
this._textViewer.endUpdates();
},
removeBreakpoint: function(lineNumber)
{
delete this._breakpoints[lineNumber];
this._textViewer.beginUpdates();
this._textViewer.removeDecoration(lineNumber, "webkit-breakpoint");
this._textViewer.removeDecoration(lineNumber, "webkit-breakpoint-disabled");
this._textViewer.removeDecoration(lineNumber, "webkit-breakpoint-conditional");
this._textViewer.endUpdates();
},
_contextMenu: function(event)
{
var contextMenu = new WebInspector.ContextMenu();
var target = event.target.enclosingNodeOrSelfWithClass("webkit-line-number");
if (target)
this._populateLineGutterContextMenu(target.lineNumber, contextMenu);
else
this._populateTextAreaContextMenu(contextMenu);
contextMenu.show(event);
},
_populateLineGutterContextMenu: function(lineNumber, contextMenu)
{
contextMenu.appendItem(WebInspector.UIString("Continue to Here"), this._delegate.continueToLine.bind(this._delegate, lineNumber));
var breakpoint = this._delegate.findBreakpoint(lineNumber);
if (!breakpoint) {
// This row doesn't have a breakpoint: We want to show Add Breakpoint and Add and Edit Breakpoint.
contextMenu.appendItem(WebInspector.UIString("Add Breakpoint"), this._delegate.setBreakpoint.bind(this._delegate, lineNumber, "", true));
function addConditionalBreakpoint()
{
this.addBreakpoint(lineNumber, true, true, true);
function didEditBreakpointCondition(committed, condition)
{
this.removeBreakpoint(lineNumber);
if (committed)
this._delegate.setBreakpoint(lineNumber, condition, true);
}
this._editBreakpointCondition(lineNumber, "", didEditBreakpointCondition.bind(this));
}
contextMenu.appendItem(WebInspector.UIString("Add Conditional Breakpoint…"), addConditionalBreakpoint.bind(this));
} else {
// This row has a breakpoint, we want to show edit and remove breakpoint, and either disable or enable.
contextMenu.appendItem(WebInspector.UIString("Remove Breakpoint"), this._delegate.removeBreakpoint.bind(this._delegate, lineNumber));
function editBreakpointCondition()
{
function didEditBreakpointCondition(committed, condition)
{
if (committed)
this._delegate.updateBreakpoint(lineNumber, condition, breakpoint.enabled);
}
this._editBreakpointCondition(lineNumber, breakpoint.condition, didEditBreakpointCondition.bind(this));
}
contextMenu.appendItem(WebInspector.UIString("Edit Breakpoint…"), editBreakpointCondition.bind(this));
function setBreakpointEnabled(enabled)
{
this._delegate.updateBreakpoint(lineNumber, breakpoint.condition, enabled);
}
if (breakpoint.enabled)
contextMenu.appendItem(WebInspector.UIString("Disable Breakpoint"), setBreakpointEnabled.bind(this, false));
else
contextMenu.appendItem(WebInspector.UIString("Enable Breakpoint"), setBreakpointEnabled.bind(this, true));
}
},
_populateTextAreaContextMenu: function(contextMenu)
{
contextMenu.appendCheckboxItem(WebInspector.UIString("De-obfuscate Source"), this._delegate.toggleFormatSourceFiles.bind(this._delegate), this._delegate.formatSourceFilesToggled());
},
_scroll: function(event)
{
this._hidePopup();
},
_mouseDown: function(event)
{
this._resetHoverTimer();
this._hidePopup();
if (event.button != 0 || event.altKey || event.ctrlKey || event.metaKey)
return;
var target = event.target.enclosingNodeOrSelfWithClass("webkit-line-number");
if (!target)
return;
var lineNumber = target.lineNumber;
var breakpoint = this._delegate.findBreakpoint(lineNumber);
if (breakpoint) {
if (event.shiftKey)
this._delegate.updateBreakpoint(lineNumber, breakpoint.condition, !breakpoint.enabled);
else
this._delegate.removeBreakpoint(lineNumber);
} else
this._delegate.setBreakpoint(lineNumber, "", true);
event.preventDefault();
},
_mouseMove: function(event)
{
// Pretend that nothing has happened.
if (this._hoverElement === event.target || event.target.hasStyleClass("source-frame-eval-expression"))
return;
this._resetHoverTimer();
// User has 500ms to reach the popup.
if (this._popup) {
var self = this;
function doHide()
{
self._hidePopup();
delete self._hidePopupTimer;
}
if (!("_hidePopupTimer" in this))
this._hidePopupTimer = setTimeout(doHide, 500);
}
this._hoverElement = event.target;
// Now that cleanup routines are set up above, leave this in case we are not on a break.
if (!this._delegate.debuggerPaused())
return;
// We are interested in identifiers and "this" keyword.
if (this._hoverElement.hasStyleClass("webkit-javascript-keyword")) {
if (this._hoverElement.textContent !== "this")
return;
} else if (!this._hoverElement.hasStyleClass("webkit-javascript-ident"))
return;
const toolTipDelay = this._popup ? 600 : 1000;
this._hoverTimer = setTimeout(this._mouseHover.bind(this, this._hoverElement), toolTipDelay);
},
_resetHoverTimer: function()
{
if (this._hoverTimer) {
clearTimeout(this._hoverTimer);
delete this._hoverTimer;
}
},
_hidePopup: function()
{
if (!this._popup)
return;
// Replace higlight element with its contents inplace.
var parentElement = this._popup.highlightElement.parentElement;
var child = this._popup.highlightElement.firstChild;
while (child) {
var nextSibling = child.nextSibling;
parentElement.insertBefore(child, this._popup.highlightElement);
child = nextSibling;
}
parentElement.removeChild(this._popup.highlightElement);
this._popup.hide();
delete this._popup;
this._delegate.releaseEvaluationResult();
},
_mouseHover: function(element)
{
delete this._hoverTimer;
var lineRow = element.enclosingNodeOrSelfWithClass("webkit-line-content");
if (!lineRow)
return;
// Collect tokens belonging to evaluated exression.
var tokens = [ element ];
var token = element.previousSibling;
while (token && (token.className === "webkit-javascript-ident" || token.className === "webkit-javascript-keyword" || token.textContent.trim() === ".")) {
tokens.push(token);
token = token.previousSibling;
}
tokens.reverse();
// Wrap them with highlight element.
var parentElement = element.parentElement;
var nextElement = element.nextSibling;
var container = document.createElement("span");
for (var i = 0; i < tokens.length; ++i)
container.appendChild(tokens[i]);
parentElement.insertBefore(container, nextElement);
this._showPopup(container);
},
_showPopup: function(element)
{
if (!this._delegate.debuggerPaused())
return;
function killHidePopupTimer()
{
if (this._hidePopupTimer) {
clearTimeout(this._hidePopupTimer);
delete this._hidePopupTimer;
// We know that we reached the popup, but we might have moved over other elements.
// Discard pending command.
this._resetHoverTimer();
}
}
function showObjectPopup(result)
{
if (result.isError() || !this._delegate.debuggerPaused())
return;
var popupContentElement = null;
if (result.type !== "object" && result.type !== "node" && result.type !== "array") {
popupContentElement = document.createElement("span");
popupContentElement.className = "monospace console-formatted-" + result.type;
popupContentElement.style.whiteSpace = "pre";
popupContentElement.textContent = result.description;
if (result.type === "string")
popupContentElement.textContent = "\"" + popupContentElement.textContent + "\"";
this._popup = new WebInspector.Popover(popupContentElement);
this._popup.show(element);
} else {
var popupContentElement = document.createElement("div");
var titleElement = document.createElement("div");
titleElement.className = "source-frame-popover-title monospace";
titleElement.textContent = result.description;
popupContentElement.appendChild(titleElement);
var section = new WebInspector.ObjectPropertiesSection(result, "", null, false);
section.expanded = true;
section.element.addStyleClass("source-frame-popover-tree");
section.headerElement.addStyleClass("hidden");
popupContentElement.appendChild(section.element);
this._popup = new WebInspector.Popover(popupContentElement);
const popupWidth = 300;
const popupHeight = 250;
this._popup.show(element, popupWidth, popupHeight);
}
this._popup.highlightElement = element;
this._popup.highlightElement.addStyleClass("source-frame-eval-expression");
popupContentElement.addEventListener("mousemove", killHidePopupTimer.bind(this), true);
}
this._delegate.evaluateInSelectedCallFrame(element.textContent, showObjectPopup.bind(this));
},
_editBreakpointCondition: function(lineNumber, condition, callback)
{
this._conditionElement = this._createConditionElement(lineNumber);
this._textViewer.addDecoration(lineNumber, this._conditionElement);
function finishEditing(committed, element, newText)
{
this._textViewer.removeDecoration(lineNumber, this._conditionElement);
delete this._conditionEditorElement;
delete this._conditionElement;
callback(committed, newText);
}
WebInspector.startEditing(this._conditionEditorElement, {
context: null,
commitHandler: finishEditing.bind(this, true),
cancelHandler: finishEditing.bind(this, false)
});
this._conditionEditorElement.value = condition;
this._conditionEditorElement.select();
},
_createConditionElement: function(lineNumber)
{
var conditionElement = document.createElement("div");
conditionElement.className = "source-frame-breakpoint-condition";
var labelElement = document.createElement("label");
labelElement.className = "source-frame-breakpoint-message";
labelElement.htmlFor = "source-frame-breakpoint-condition";
labelElement.appendChild(document.createTextNode(WebInspector.UIString("The breakpoint on line %d will stop only if this expression is true:", lineNumber)));
conditionElement.appendChild(labelElement);
var editorElement = document.createElement("input");
editorElement.id = "source-frame-breakpoint-condition";
editorElement.className = "monospace";
editorElement.type = "text";
conditionElement.appendChild(editorElement);
this._conditionEditorElement = editorElement;
return conditionElement;
},
resize: function()
{
this._textViewer.resize();
},
commitEditing: function(callback)
{
if (!this._viewerState) {
// No editing was actually done.
this._delegate.setScriptSourceIsBeingEdited(false);
callback();
return;
}
function didEditContent(error)
{
if (error) {
if (error.data && error.data[0]) {
WebInspector.log(error.data[0], WebInspector.ConsoleMessage.MessageLevel.Error);
WebInspector.showConsole();
}
callback(error);
return;
}
var newBreakpoints = {};
for (var lineNumber in this._breakpoints) {
newBreakpoints[lineNumber] = this._breakpoints[lineNumber];
this.removeBreakpoint(Number(lineNumber));
}
for (var lineNumber in this._viewerState.breakpoints)
this._delegate.removeBreakpoint(Number(lineNumber));
for (var lineNumber in newBreakpoints) {
var breakpoint = newBreakpoints[lineNumber];
this._delegate.setBreakpoint(Number(lineNumber), breakpoint.condition, breakpoint.enabled);
}
delete this._viewerState;
this._delegate.setScriptSourceIsBeingEdited(false);
callback();
}
this.editContent(this._textModel.text, didEditContent.bind(this));
},
editContent: function(newContent, callback)
{
this._delegate.editScriptSource(newContent, callback);
},
cancelEditing: function()
{
this._restoreViewerState();
this._delegate.setScriptSourceIsBeingEdited(false);
}
}
WebInspector.SourceFrame.prototype.__proto__ = WebInspector.TextViewerDelegate.prototype;
WebInspector.SourceFrameDelegate = function()
{
}
WebInspector.SourceFrameDelegate.prototype = {
requestContent: function(callback)
{
// Should be implemented by subclasses.
},
debuggingSupported: function()
{
return false;
},
setBreakpoint: function(lineNumber, condition, enabled)
{
// Should be implemented by subclasses.
},
removeBreakpoint: function(lineNumber)
{
// Should be implemented by subclasses.
},
updateBreakpoint: function(lineNumber, condition, enabled)
{
// Should be implemented by subclasses.
},
findBreakpoint: function(lineNumber)
{
// Should be implemented by subclasses.
},
continueToLine: function(lineNumber)
{
// Should be implemented by subclasses.
},
canEditScriptSource: function()
{
return false;
},
editScriptSource: function(text, callback)
{
// Should be implemented by subclasses.
},
setScriptSourceIsBeingEdited: function(inEditMode)
{
// Should be implemented by subclasses.
},
debuggerPaused: function()
{
// Should be implemented by subclasses.
},
evaluateInSelectedCallFrame: function(string)
{
// Should be implemented by subclasses.
},
releaseEvaluationResult: function()
{
// Should be implemented by subclasses.
},
toggleFormatSourceFiles: function()
{
// Should be implemented by subclasses.
},
formatSourceFilesToggled: function()
{
// Should be implemented by subclasses.
}
}