2010-05-02 Joseph Pecoraro <joepeck@webkit.org>
[WebKit-https.git] / WebCore / inspector / front-end / SourceFrame.js
1 /*
2  * Copyright (C) 2009 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
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
13  * distribution.
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.
17  *
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.
29  */
30
31 WebInspector.SourceFrame = function(parentElement, addBreakpointDelegate, removeBreakpointDelegate, editDelegate)
32 {
33     this._parentElement = parentElement;
34
35     this._textModel = new WebInspector.TextEditorModel();
36     this._textModel.replaceTabsWithSpaces = true;
37
38     this._messages = [];
39     this._rowMessages = {};
40     this._messageBubbles = {};
41     this.breakpoints = [];
42
43     this._loaded = false;
44
45     this._addBreakpointDelegate = addBreakpointDelegate;
46     this._removeBreakpointDelegate = removeBreakpointDelegate;
47     this._editDelegate = editDelegate;
48     this._popoverObjectGroup = "popover";
49 }
50
51 WebInspector.SourceFrame.prototype = {
52
53     set visible(visible)
54     {
55         this._visible = visible;
56         this._createViewerIfNeeded();
57         
58         if (!visible) {
59             this._hidePopup();
60             if (this._textViewer)
61                 this._textViewer.freeCachedElements();
62         }
63     },
64
65     get executionLine()
66     {
67         return this._executionLine;
68     },
69
70     set executionLine(x)
71     {
72         if (this._executionLine === x)
73             return;
74
75         var previousLine = this._executionLine;
76         this._executionLine = x;
77
78         if (this._textViewer)
79             this._updateExecutionLine(previousLine);
80     },
81
82     revealLine: function(lineNumber)
83     {
84         if (this._textViewer)
85             this._textViewer.revealLine(lineNumber - 1, 0);
86         else
87             this._lineNumberToReveal = lineNumber;
88     },
89
90     addBreakpoint: function(breakpoint)
91     {
92         this.breakpoints.push(breakpoint);
93         breakpoint.addEventListener("enabled", this._breakpointChanged, this);
94         breakpoint.addEventListener("disabled", this._breakpointChanged, this);
95         breakpoint.addEventListener("condition-changed", this._breakpointChanged, this);
96         if (this._textViewer)
97             this._addBreakpointToSource(breakpoint);
98     },
99
100     removeBreakpoint: function(breakpoint)
101     {
102         this.breakpoints.remove(breakpoint);
103         breakpoint.removeEventListener("enabled", null, this);
104         breakpoint.removeEventListener("disabled", null, this);
105         breakpoint.removeEventListener("condition-changed", null, this);
106         if (this._textViewer)
107             this._removeBreakpointFromSource(breakpoint);
108     },
109
110     addMessage: function(msg)
111     {
112         // Don't add the message if there is no message or valid line or if the msg isn't an error or warning.
113         if (!msg.message || msg.line <= 0 || !msg.isErrorOrWarning())
114             return;
115         this._messages.push(msg)
116         if (this._textViewer)
117             this._addMessageToSource(msg);
118     },
119
120     clearMessages: function()
121     {
122         for (var line in this._messageBubbles) {
123             var bubble = this._messageBubbles[line];
124             bubble.parentNode.removeChild(bubble);
125         }
126
127         this._messages = [];
128         this._rowMessages = {};
129         this._messageBubbles = {};
130         if (this._textViewer)
131             this._textViewer.resize();
132     },
133
134     sizeToFitContentHeight: function()
135     {
136         if (this._textViewer)
137             this._textViewer.revalidateDecorationsAndPaint();
138     },
139
140     setContent: function(mimeType, content, url)
141     {
142         this._loaded = true;
143         this._textModel.setText(null, content);
144         this._mimeType = mimeType;
145         this._url = url;
146         this._createViewerIfNeeded();
147     },
148
149     updateContent: function(content)
150     {
151         this._textModel.setText(null, content);
152     },
153
154     get textModel()
155     {
156         return this._textModel;
157     },
158
159     highlightLine: function(line)
160     {
161         if (this._textViewer)
162             this._textViewer.highlightLine(line - 1);
163         else
164             this._lineToHighlight = line;
165     },
166
167     _createViewerIfNeeded: function()
168     {
169         if (!this._visible || !this._loaded || this._textViewer)
170             return;
171
172         this._textViewer = new WebInspector.TextViewer(this._textModel, WebInspector.platform, this._url);
173         var element = this._textViewer.element;
174         element.addEventListener("contextmenu", this._contextMenu.bind(this), true);
175         element.addEventListener("mousedown", this._mouseDown.bind(this), true);
176         element.addEventListener("mousemove", this._mouseMove.bind(this), true);
177         element.addEventListener("scroll", this._scroll.bind(this), true);
178         this._parentElement.appendChild(element);
179
180         this._needsProgramCounterImage = true;
181         this._needsBreakpointImages = true;
182
183         this._textViewer.beginUpdates();
184
185         this._textViewer.mimeType = this._mimeType;
186         this._addExistingMessagesToSource();
187         this._addExistingBreakpointsToSource();
188         this._updateExecutionLine();
189         this._textViewer.resize();
190
191         if (this._lineNumberToReveal) {
192             this.revealLine(this._lineNumberToReveal);
193             delete this._lineNumberToReveal;
194         }
195
196         if (this._pendingMarkRange) {
197             var range = this._pendingMarkRange;
198             this.markAndRevealRange(range);
199             delete this._pendingMarkRange;
200         }
201
202         if (this._lineToHighlight) {
203             this.highlightLine(this._lineToHighlight);
204             delete this._lineToHighlight;
205         }
206         this._textViewer.endUpdates();
207         if (this._editDelegate)
208             this._textViewer.editCallback = this._editDelegate;
209     },
210
211     findSearchMatches: function(query)
212     {
213         var ranges = [];
214
215         // First do case-insensitive search.
216         var regexObject = createSearchRegex(query);
217         this._collectRegexMatches(regexObject, ranges);
218
219         // Then try regex search if user knows the / / hint.
220         try {
221             if (/^\/.*\/$/.test(query))
222                 this._collectRegexMatches(new RegExp(query.substring(1, query.length - 1)), ranges);
223         } catch (e) {
224             // Silent catch.
225         }
226         return ranges;
227     },
228
229     _collectRegexMatches: function(regexObject, ranges)
230     {
231         for (var i = 0; i < this._textModel.linesCount; ++i) {
232             var line = this._textModel.line(i);
233             var offset = 0;
234             do {
235                 var match = regexObject.exec(line);
236                 if (match) {
237                     ranges.push(new WebInspector.TextRange(i, offset + match.index, i, offset + match.index + match[0].length));
238                     offset += match.index + 1;
239                     line = line.substring(match.index + 1);
240                 }
241             } while (match)
242         }
243         return ranges;
244     },
245
246     markAndRevealRange: function(range)
247     {
248         if (this._textViewer)
249             this._textViewer.markAndRevealRange(range);
250         else
251             this._pendingMarkRange = range;
252     },
253
254     clearMarkedRange: function()
255     {
256         if (this._textViewer) {
257             this._textViewer.markAndRevealRange(null);
258         } else
259             delete this._pendingMarkRange;
260     },
261
262     _incrementMessageRepeatCount: function(msg, repeatDelta)
263     {
264         if (!msg._resourceMessageLineElement)
265             return;
266
267         if (!msg._resourceMessageRepeatCountElement) {
268             var repeatedElement = document.createElement("span");
269             msg._resourceMessageLineElement.appendChild(repeatedElement);
270             msg._resourceMessageRepeatCountElement = repeatedElement;
271         }
272
273         msg.repeatCount += repeatDelta;
274         msg._resourceMessageRepeatCountElement.textContent = WebInspector.UIString(" (repeated %d times)", msg.repeatCount);
275     },
276
277     _breakpointChanged: function(event)
278     {
279         var breakpoint = event.target;
280         var lineNumber = breakpoint.line - 1;
281         if (lineNumber >= this._textModel.linesCount)
282             return;
283
284         if (breakpoint.enabled)
285             this._textViewer.removeDecoration(lineNumber, "webkit-breakpoint-disabled");
286         else
287             this._textViewer.addDecoration(lineNumber, "webkit-breakpoint-disabled");
288
289         if (breakpoint.condition)
290             this._textViewer.addDecoration(lineNumber, "webkit-breakpoint-conditional");
291         else
292             this._textViewer.removeDecoration(lineNumber, "webkit-breakpoint-conditional");
293     },
294
295     _updateExecutionLine: function(previousLine)
296     {
297         if (previousLine) {
298             if (previousLine - 1 < this._textModel.linesCount)
299                 this._textViewer.removeDecoration(previousLine - 1, "webkit-execution-line");
300         }
301
302         if (!this._executionLine)
303             return;
304
305         if (this._executionLine < this._textModel.linesCount)
306             this._textViewer.addDecoration(this._executionLine - 1, "webkit-execution-line");
307     },
308
309     _addExistingMessagesToSource: function()
310     {
311         var length = this._messages.length;
312         for (var i = 0; i < length; ++i)
313             this._addMessageToSource(this._messages[i]);
314     },
315
316     _addMessageToSource: function(msg)
317     {
318         if (msg.line >= this._textModel.linesCount)
319             return;
320
321         var messageBubbleElement = this._messageBubbles[msg.line];
322         if (!messageBubbleElement || messageBubbleElement.nodeType !== Node.ELEMENT_NODE || !messageBubbleElement.hasStyleClass("webkit-html-message-bubble")) {
323             messageBubbleElement = document.createElement("div");
324             messageBubbleElement.className = "webkit-html-message-bubble";
325             this._messageBubbles[msg.line] = messageBubbleElement;
326             this._textViewer.addDecoration(msg.line - 1, messageBubbleElement);
327         }
328
329         var rowMessages = this._rowMessages[msg.line];
330         if (!rowMessages) {
331             rowMessages = [];
332             this._rowMessages[msg.line] = rowMessages;
333         }
334
335         for (var i = 0; i < rowMessages.length; ++i) {
336             if (rowMessages[i].isEqual(msg, true)) {
337                 this._incrementMessageRepeatCount(rowMessages[i], msg.repeatDelta);
338                 return;
339             }
340         }
341
342         rowMessages.push(msg);
343
344         var imageURL;
345         switch (msg.level) {
346             case WebInspector.ConsoleMessage.MessageLevel.Error:
347                 messageBubbleElement.addStyleClass("webkit-html-error-message");
348                 imageURL = "Images/errorIcon.png";
349                 break;
350             case WebInspector.ConsoleMessage.MessageLevel.Warning:
351                 messageBubbleElement.addStyleClass("webkit-html-warning-message");
352                 imageURL = "Images/warningIcon.png";
353                 break;
354         }
355
356         var messageLineElement = document.createElement("div");
357         messageLineElement.className = "webkit-html-message-line";
358         messageBubbleElement.appendChild(messageLineElement);
359
360         // Create the image element in the Inspector's document so we can use relative image URLs.
361         var image = document.createElement("img");
362         image.src = imageURL;
363         image.className = "webkit-html-message-icon";
364         messageLineElement.appendChild(image);
365         messageLineElement.appendChild(document.createTextNode(msg.message));
366
367         msg._resourceMessageLineElement = messageLineElement;
368     },
369
370     _addExistingBreakpointsToSource: function()
371     {
372         for (var i = 0; i < this.breakpoints.length; ++i)
373             this._addBreakpointToSource(this.breakpoints[i]);
374     },
375
376     _addBreakpointToSource: function(breakpoint)
377     {
378         var lineNumber = breakpoint.line - 1;
379         if (lineNumber >= this._textModel.linesCount)
380             return;
381
382         this._textModel.setAttribute(lineNumber, "breakpoint", breakpoint);
383         breakpoint.sourceText = this._textModel.line(breakpoint.line - 1);
384
385         this._textViewer.beginUpdates();
386         this._textViewer.addDecoration(lineNumber, "webkit-breakpoint");
387         if (!breakpoint.enabled)
388             this._textViewer.addDecoration(lineNumber, "webkit-breakpoint-disabled");
389         if (breakpoint.condition)
390             this._textViewer.addDecoration(lineNumber, "webkit-breakpoint-conditional");
391         this._textViewer.endUpdates();
392     },
393
394     _removeBreakpointFromSource: function(breakpoint)
395     {
396         var lineNumber = breakpoint.line - 1;
397         this._textViewer.beginUpdates();
398         this._textModel.removeAttribute(lineNumber, "breakpoint");
399         this._textViewer.removeDecoration(lineNumber, "webkit-breakpoint");
400         this._textViewer.removeDecoration(lineNumber, "webkit-breakpoint-disabled");
401         this._textViewer.removeDecoration(lineNumber, "webkit-breakpoint-conditional");
402         this._textViewer.endUpdates();
403     },
404
405     _contextMenu: function(event)
406     {
407         var target = event.target.enclosingNodeOrSelfWithClass("webkit-line-number");
408         if (!target)
409             return;
410         var row = target.parentElement;
411
412         var lineNumber = row.lineNumber;
413         var contextMenu = new WebInspector.ContextMenu();
414
415         var breakpoint = this._textModel.getAttribute(lineNumber, "breakpoint");
416         if (!breakpoint) {
417             // This row doesn't have a breakpoint: We want to show Add Breakpoint and Add and Edit Breakpoint.
418             contextMenu.appendItem(WebInspector.UIString("Add Breakpoint"), this._addBreakpointDelegate.bind(this, lineNumber + 1));
419
420             function addConditionalBreakpoint() 
421             {
422                 this._addBreakpointDelegate(lineNumber + 1);
423                 var breakpoint = this._textModel.getAttribute(lineNumber, "breakpoint");
424                 if (breakpoint)
425                     this._editBreakpointCondition(breakpoint);
426             }
427
428             contextMenu.appendItem(WebInspector.UIString("Add Conditional Breakpoint..."), addConditionalBreakpoint.bind(this));
429         } else {
430             // This row has a breakpoint, we want to show edit and remove breakpoint, and either disable or enable.
431             contextMenu.appendItem(WebInspector.UIString("Remove Breakpoint"), WebInspector.panels.scripts.removeBreakpoint.bind(WebInspector.panels.scripts, breakpoint));
432             contextMenu.appendItem(WebInspector.UIString("Edit Breakpoint..."), this._editBreakpointCondition.bind(this, breakpoint));
433             if (breakpoint.enabled)
434                 contextMenu.appendItem(WebInspector.UIString("Disable Breakpoint"), function() { breakpoint.enabled = false; });
435             else
436                 contextMenu.appendItem(WebInspector.UIString("Enable Breakpoint"), function() { breakpoint.enabled = true; });
437         }
438         contextMenu.show(event);
439     },
440
441     _scroll: function(event)
442     {
443         this._hidePopup();
444     },
445
446     _mouseDown: function(event)
447     {
448         this._resetHoverTimer();
449         this._hidePopup();
450         if (event.button != 0 || event.altKey || event.ctrlKey || event.metaKey)
451             return;
452         var target = event.target.enclosingNodeOrSelfWithClass("webkit-line-number");
453         if (!target)
454             return;
455         var row = target.parentElement;
456
457         var lineNumber = row.lineNumber;
458
459         var breakpoint = this._textModel.getAttribute(lineNumber, "breakpoint");
460         if (breakpoint) {
461             if (event.shiftKey)
462                 breakpoint.enabled = !breakpoint.enabled;
463             else
464                 this._removeBreakpointDelegate(breakpoint);
465         } else
466             this._addBreakpointDelegate(lineNumber + 1);
467         event.preventDefault();
468     },
469
470     _mouseMove: function(event)
471     {
472         // Pretend that nothing has happened.
473         if (this._hoverElement === event.target || event.target.hasStyleClass("source-frame-eval-expression"))
474             return;
475
476         this._resetHoverTimer();
477         // User has 500ms to reach the popup.
478         if (this._popup) {
479             var self = this;
480             function doHide()
481             {
482                 self._hidePopup();
483                 delete self._hidePopupTimer;
484             }
485             this._hidePopupTimer = setTimeout(doHide, 500);
486         }
487
488         this._hoverElement = event.target;
489
490         // Now that cleanup routines are set up above, leave this in case we are not on a break.
491         if (!WebInspector.panels.scripts || !WebInspector.panels.scripts.paused)
492             return;
493
494         // We are interested in identifiers and "this" keyword.
495         if (this._hoverElement.hasStyleClass("webkit-javascript-keyword")) {
496             if (this._hoverElement.textContent !== "this")
497                 return;
498         } else if (!this._hoverElement.hasStyleClass("webkit-javascript-ident"))
499             return;
500
501         const toolTipDelay = this._popup ? 600 : 1000;
502         this._hoverTimer = setTimeout(this._mouseHover.bind(this, this._hoverElement), toolTipDelay);
503     },
504
505     _resetHoverTimer: function()
506     {
507         if (this._hoverTimer) {
508             clearTimeout(this._hoverTimer);
509             delete this._hoverTimer;
510         }
511     },
512
513     _hidePopup: function()
514     {
515         if (!this._popup)
516             return;
517
518         // Replace higlight element with its contents inplace.
519         var parentElement = this._popup.highlightElement.parentElement;
520         var child = this._popup.highlightElement.firstChild;
521         while (child) {
522             var nextSibling = child.nextSibling;
523             parentElement.insertBefore(child, this._popup.highlightElement);
524             child = nextSibling;
525         }
526         parentElement.removeChild(this._popup.highlightElement);
527
528         this._popup.hide();
529         delete this._popup;
530         InspectorBackend.releaseWrapperObjectGroup(0, this._popoverObjectGroup);
531     },
532
533     _mouseHover: function(element)
534     {
535         delete this._hoverTimer;
536
537         if (!WebInspector.panels.scripts || !WebInspector.panels.scripts.paused)
538             return;
539
540         var lineRow = element.enclosingNodeOrSelfWithNodeName("tr");
541         if (!lineRow)
542             return;
543
544         // Collect tokens belonging to evaluated exression.
545         var tokens = [ element ];
546         var token = element.previousSibling;
547         while (token && (token.className === "webkit-javascript-ident" || token.className === "webkit-javascript-keyword" || token.textContent.trim() === ".")) {
548             tokens.push(token);
549             token = token.previousSibling;
550         }
551         tokens.reverse();
552
553         // Wrap them with highlight element.
554         var parentElement = element.parentElement;
555         var nextElement = element.nextSibling;
556         var container = document.createElement("span");
557         for (var i = 0; i < tokens.length; ++i)
558             container.appendChild(tokens[i]);
559         parentElement.insertBefore(container, nextElement);
560         this._showPopup(container);
561     },
562
563     _showPopup: function(element)
564     {
565         function killHidePopupTimer()
566         {
567             if (this._hidePopupTimer) {
568                 clearTimeout(this._hidePopupTimer);
569                 delete this._hidePopupTimer;
570
571                 // We know that we reached the popup, but we might have moved over other elements.
572                 // Discard pending command.
573                 this._resetHoverTimer();
574             }
575         }
576
577         function showObjectPopup(result)
578         {
579             if (!WebInspector.panels.scripts.paused)
580                 return;
581
582             var popupContentElement = null;
583             if (result.type !== "object" && result.type !== "node" && result.type !== "array") {
584                 popupContentElement = document.createElement("span");
585                 popupContentElement.className = "monospace";
586                 popupContentElement.style.whiteSpace = "pre";
587                 popupContentElement.textContent = result.description;
588                 this._popup = new WebInspector.Popover(popupContentElement);
589                 this._popup.show(element);
590             } else {
591                 var popupContentElement = document.createElement("div");
592
593                 var titleElement = document.createElement("div");
594                 titleElement.className = "source-frame-popover-title monospace";
595                 titleElement.textContent = result.description;
596                 popupContentElement.appendChild(titleElement);
597
598                 var section = new WebInspector.ObjectPropertiesSection(result, "", null, false);
599                 section.expanded = true;
600                 section.element.addStyleClass("source-frame-popover-tree");
601                 section.headerElement.addStyleClass("hidden");
602                 popupContentElement.appendChild(section.element);
603
604                 this._popup = new WebInspector.Popover(popupContentElement);
605                 const popupWidth = 300;
606                 const popupHeight = 250;
607                 this._popup.show(element, popupWidth, popupHeight);
608             }
609             this._popup.highlightElement = element;
610             this._popup.highlightElement.addStyleClass("source-frame-eval-expression");
611             popupContentElement.addEventListener("mousemove", killHidePopupTimer.bind(this), true);
612         }
613
614         function evaluateCallback(result, exception)
615         {
616             if (exception)
617                 return;
618             if (!WebInspector.panels.scripts.paused)
619                 return;
620             showObjectPopup.call(this, result);
621         }
622         WebInspector.panels.scripts.evaluateInSelectedCallFrame(element.textContent, false, this._popoverObjectGroup, evaluateCallback.bind(this));
623     },
624
625     _editBreakpointCondition: function(breakpoint)
626     {
627         this._showBreakpointConditionPopup(breakpoint.line);
628
629         function committed(element, newText)
630         {
631             breakpoint.condition = newText;
632             dismissed.call(this);
633         }
634
635         function dismissed()
636         {
637             if (this._conditionElement)
638                 this._textViewer.removeDecoration(breakpoint.line - 1, this._conditionElement);
639             delete this._conditionEditorElement;
640             delete this._conditionElement;
641         }
642
643         var dismissedHandler = dismissed.bind(this);
644         this._conditionEditorElement.addEventListener("blur", dismissedHandler, false);
645
646         WebInspector.startEditing(this._conditionEditorElement, committed.bind(this), dismissedHandler);
647         this._conditionEditorElement.value = breakpoint.condition;
648         this._conditionEditorElement.select();
649     },
650
651     _showBreakpointConditionPopup: function(lineNumber)
652     {
653         this._conditionElement = this._createConditionElement(lineNumber);
654         this._textViewer.addDecoration(lineNumber - 1, this._conditionElement);
655     },
656
657     _createConditionElement: function(lineNumber)
658     {
659         var conditionElement = document.createElement("div");
660         conditionElement.className = "source-frame-breakpoint-condition";
661
662         var labelElement = document.createElement("label");
663         labelElement.className = "source-frame-breakpoint-message";
664         labelElement.htmlFor = "source-frame-breakpoint-condition";
665         labelElement.appendChild(document.createTextNode(WebInspector.UIString("The breakpoint on line %d will stop only if this expression is true:", lineNumber)));
666         conditionElement.appendChild(labelElement);
667
668         var editorElement = document.createElement("input");
669         editorElement.id = "source-frame-breakpoint-condition";
670         editorElement.className = "monospace";
671         editorElement.type = "text"
672         conditionElement.appendChild(editorElement);
673         this._conditionEditorElement = editorElement;
674
675         return conditionElement;
676     },
677
678     _evalSelectionInCallFrame: function(event)
679     {
680         if (!WebInspector.panels.scripts || !WebInspector.panels.scripts.paused)
681             return;
682
683         var selection = this.element.contentWindow.getSelection();
684         if (!selection.rangeCount)
685             return;
686
687         var expression = selection.getRangeAt(0).toString().trim();
688         WebInspector.panels.scripts.evaluateInSelectedCallFrame(expression, false, "console", function(result, exception) {
689             WebInspector.showConsole();
690             var commandMessage = new WebInspector.ConsoleCommand(expression);
691             WebInspector.console.addMessage(commandMessage);
692             WebInspector.console.addMessage(new WebInspector.ConsoleCommandResult(result, exception, commandMessage));
693         });
694     },
695
696     resize: function()
697     {
698         if (this._textViewer)
699             this._textViewer.resize();
700     }
701 }
702
703
704 WebInspector.SourceFrame.prototype.__proto__ = WebInspector.Object.prototype;