2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 WebInspector.ScriptsPanel = function()
28 WebInspector.Panel.call(this, "scripts");
30 this._presentationModel = new WebInspector.DebuggerPresentationModel();
32 this.topStatusBar = document.createElement("div");
33 this.topStatusBar.className = "status-bar";
34 this.topStatusBar.id = "scripts-status-bar";
35 this.element.appendChild(this.topStatusBar);
37 this.backButton = document.createElement("button");
38 this.backButton.className = "status-bar-item";
39 this.backButton.id = "scripts-back";
40 this.backButton.title = WebInspector.UIString("Show the previous script resource.");
41 this.backButton.disabled = true;
42 this.backButton.appendChild(document.createElement("img"));
43 this.backButton.addEventListener("click", this._goBack.bind(this), false);
44 this.topStatusBar.appendChild(this.backButton);
46 this.forwardButton = document.createElement("button");
47 this.forwardButton.className = "status-bar-item";
48 this.forwardButton.id = "scripts-forward";
49 this.forwardButton.title = WebInspector.UIString("Show the next script resource.");
50 this.forwardButton.disabled = true;
51 this.forwardButton.appendChild(document.createElement("img"));
52 this.forwardButton.addEventListener("click", this._goForward.bind(this), false);
53 this.topStatusBar.appendChild(this.forwardButton);
55 this._filesSelectElement = document.createElement("select");
56 this._filesSelectElement.className = "status-bar-item";
57 this._filesSelectElement.id = "scripts-files";
58 this._filesSelectElement.addEventListener("change", this._filesSelectChanged.bind(this), false);
59 this.topStatusBar.appendChild(this._filesSelectElement);
61 this.functionsSelectElement = document.createElement("select");
62 this.functionsSelectElement.className = "status-bar-item";
63 this.functionsSelectElement.id = "scripts-functions";
65 // FIXME: append the functions select element to the top status bar when it is implemented.
66 // this.topStatusBar.appendChild(this.functionsSelectElement);
68 this.formatButton = document.createElement("button");
69 this.formatButton.className = "status-bar-item";
70 this.formatButton.id = "format-script";
71 this.formatButton.title = WebInspector.UIString("Format script.");
72 this.formatButton.appendChild(document.createElement("img"));
73 this.formatButton.addEventListener("click", this._formatScript.bind(this), false);
74 if (Preferences.debugMode)
75 this.topStatusBar.appendChild(this.formatButton);
77 this.sidebarButtonsElement = document.createElement("div");
78 this.sidebarButtonsElement.id = "scripts-sidebar-buttons";
79 this.topStatusBar.appendChild(this.sidebarButtonsElement);
81 this.pauseButton = document.createElement("button");
82 this.pauseButton.className = "status-bar-item";
83 this.pauseButton.id = "scripts-pause";
84 this.pauseButton.title = WebInspector.UIString("Pause script execution.");
85 this.pauseButton.disabled = true;
86 this.pauseButton.appendChild(document.createElement("img"));
87 this.pauseButton.addEventListener("click", this._togglePause.bind(this), false);
88 this.sidebarButtonsElement.appendChild(this.pauseButton);
90 this.stepOverButton = document.createElement("button");
91 this.stepOverButton.className = "status-bar-item";
92 this.stepOverButton.id = "scripts-step-over";
93 this.stepOverButton.title = WebInspector.UIString("Step over next function call.");
94 this.stepOverButton.disabled = true;
95 this.stepOverButton.addEventListener("click", this._stepOverClicked.bind(this), false);
96 this.stepOverButton.appendChild(document.createElement("img"));
97 this.sidebarButtonsElement.appendChild(this.stepOverButton);
99 this.stepIntoButton = document.createElement("button");
100 this.stepIntoButton.className = "status-bar-item";
101 this.stepIntoButton.id = "scripts-step-into";
102 this.stepIntoButton.title = WebInspector.UIString("Step into next function call.");
103 this.stepIntoButton.disabled = true;
104 this.stepIntoButton.addEventListener("click", this._stepIntoClicked.bind(this), false);
105 this.stepIntoButton.appendChild(document.createElement("img"));
106 this.sidebarButtonsElement.appendChild(this.stepIntoButton);
108 this.stepOutButton = document.createElement("button");
109 this.stepOutButton.className = "status-bar-item";
110 this.stepOutButton.id = "scripts-step-out";
111 this.stepOutButton.title = WebInspector.UIString("Step out of current function.");
112 this.stepOutButton.disabled = true;
113 this.stepOutButton.addEventListener("click", this._stepOutClicked.bind(this), false);
114 this.stepOutButton.appendChild(document.createElement("img"));
115 this.sidebarButtonsElement.appendChild(this.stepOutButton);
117 this.toggleBreakpointsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Deactivate all breakpoints."), "toggle-breakpoints");
118 this.toggleBreakpointsButton.toggled = true;
119 this.toggleBreakpointsButton.addEventListener("click", this.toggleBreakpointsClicked.bind(this), false);
120 this.sidebarButtonsElement.appendChild(this.toggleBreakpointsButton.element);
122 this.debuggerStatusElement = document.createElement("div");
123 this.debuggerStatusElement.id = "scripts-debugger-status";
124 this.sidebarButtonsElement.appendChild(this.debuggerStatusElement);
126 this.viewsContainerElement = document.createElement("div");
127 this.viewsContainerElement.id = "script-resource-views";
129 this.sidebarElement = document.createElement("div");
130 this.sidebarElement.id = "scripts-sidebar";
132 this.sidebarResizeElement = document.createElement("div");
133 this.sidebarResizeElement.className = "sidebar-resizer-vertical";
134 this.sidebarResizeElement.addEventListener("mousedown", this._startSidebarResizeDrag.bind(this), false);
136 this.sidebarResizeWidgetElement = document.createElement("div");
137 this.sidebarResizeWidgetElement.id = "scripts-sidebar-resizer-widget";
138 this.sidebarResizeWidgetElement.addEventListener("mousedown", this._startSidebarResizeDrag.bind(this), false);
139 this.topStatusBar.appendChild(this.sidebarResizeWidgetElement);
141 this.sidebarPanes = {};
142 this.sidebarPanes.watchExpressions = new WebInspector.WatchExpressionsSidebarPane();
143 this.sidebarPanes.callstack = new WebInspector.CallStackSidebarPane();
144 this.sidebarPanes.scopechain = new WebInspector.ScopeChainSidebarPane();
145 this.sidebarPanes.jsBreakpoints = new WebInspector.JavaScriptBreakpointsSidebarPane();
146 if (Preferences.nativeInstrumentationEnabled) {
147 this.sidebarPanes.domBreakpoints = WebInspector.createDOMBreakpointsSidebarPane();
148 this.sidebarPanes.xhrBreakpoints = WebInspector.createXHRBreakpointsSidebarPane();
149 this.sidebarPanes.eventListenerBreakpoints = new WebInspector.EventListenerBreakpointsSidebarPane();
152 this.sidebarPanes.workers = new WebInspector.WorkersSidebarPane();
154 for (var pane in this.sidebarPanes)
155 this.sidebarElement.appendChild(this.sidebarPanes[pane].element);
157 this.sidebarPanes.callstack.expanded = true;
158 this.sidebarPanes.callstack.addEventListener("call frame selected", this._callFrameSelected, this);
160 this.sidebarPanes.scopechain.expanded = true;
161 this.sidebarPanes.jsBreakpoints.expanded = true;
163 var panelEnablerHeading = WebInspector.UIString("You need to enable debugging before you can use the Scripts panel.");
164 var panelEnablerDisclaimer = WebInspector.UIString("Enabling debugging will make scripts run slower.");
165 var panelEnablerButton = WebInspector.UIString("Enable Debugging");
167 this.panelEnablerView = new WebInspector.PanelEnablerView("scripts", panelEnablerHeading, panelEnablerDisclaimer, panelEnablerButton);
168 this.panelEnablerView.addEventListener("enable clicked", this._enableDebugging, this);
170 this.element.appendChild(this.panelEnablerView.element);
171 this.element.appendChild(this.viewsContainerElement);
172 this.element.appendChild(this.sidebarElement);
173 this.element.appendChild(this.sidebarResizeElement);
175 this.enableToggleButton = new WebInspector.StatusBarButton("", "enable-toggle-status-bar-item");
176 this.enableToggleButton.addEventListener("click", this._toggleDebugging.bind(this), false);
177 if (Preferences.debuggerAlwaysEnabled)
178 this.enableToggleButton.element.addStyleClass("hidden");
180 this._pauseOnExceptionButton = new WebInspector.StatusBarButton("", "scripts-pause-on-exceptions-status-bar-item", 3);
181 this._pauseOnExceptionButton.addEventListener("click", this._togglePauseOnExceptions.bind(this), false);
183 this._registerShortcuts();
185 this._debuggerEnabled = Preferences.debuggerAlwaysEnabled;
189 WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ParsedScriptSource, this._parsedScriptSource, this);
190 WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.FailedToParseScriptSource, this._failedToParseScriptSource, this);
191 WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ScriptSourceChanged, this._scriptSourceChanged, this);
192 WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPaused, this);
193 WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerResumed, this._debuggerResumed, this);
194 this._presentationModel.addEventListener(WebInspector.DebuggerPresentationModel.Events.BreakpointAdded, this._breakpointAdded, this);
195 this._presentationModel.addEventListener(WebInspector.DebuggerPresentationModel.Events.BreakpointRemoved, this._breakpointRemoved, this);
198 // Keep these in sync with WebCore::ScriptDebugServer
199 WebInspector.ScriptsPanel.PauseOnExceptionsState = {
200 DontPauseOnExceptions : 0,
201 PauseOnAllExceptions : 1,
202 PauseOnUncaughtExceptions: 2
205 WebInspector.ScriptsPanel.prototype = {
206 get toolbarItemLabel()
208 return WebInspector.UIString("Scripts");
213 return [this.enableToggleButton.element, this._pauseOnExceptionButton.element];
216 get defaultFocusedElement()
218 return this._filesSelectElement;
228 WebInspector.Panel.prototype.show.call(this);
229 this.sidebarResizeElement.style.right = (this.sidebarElement.offsetWidth - 3) + "px";
231 if (this.visibleView)
232 this.visibleView.show(this.viewsContainerElement);
237 if (this.visibleView)
238 this.visibleView.hide();
239 WebInspector.Panel.prototype.hide.call(this);
242 get breakpointsActivated()
244 return this.toggleBreakpointsButton.toggled;
247 _parsedScriptSource: function(event)
249 this._addScript(event.data);
252 _failedToParseScriptSource: function(event)
254 this._addScript(event.data);
257 _scriptSourceChanged: function(event)
259 var sourceID = event.data.sourceID;
260 var oldSource = event.data.oldSource;
262 var script = WebInspector.debuggerModel.scriptForSourceID(sourceID);
263 if (script.resource) {
264 var revertHandle = WebInspector.debuggerModel.editScriptSource.bind(WebInspector.debuggerModel, sourceID, oldSource);
265 script.resource.setContent(script.source, revertHandle);
268 var sourceName = this._sourceNameForScript(script);
269 this._recreateSourceFrame(sourceName);
271 var callFrames = WebInspector.debuggerModel.callFrames;
272 if (callFrames.length)
273 this._debuggerPaused({ data: { callFrames: callFrames } });
276 _addScript: function(script)
278 if (!script.sourceURL) {
279 // Anonymous scripts are shown only when stepping.
283 var resource = this._resourceForURL(script.sourceURL);
285 if (resource.finished) {
286 // Resource is finished, bind the script right away.
287 script.resource = resource;
289 // Add resource url to files select if not already added while debugging inlined scripts.
290 if (!(resource.url in this._sourceNameToFilesSelectOption))
291 this._addOptionToFilesSelectAndShowSourceFrameIfNeeded(resource.url);
293 // Resource is not finished, bind the script later.
294 if (!resource._scriptsPendingResourceLoad) {
295 resource._scriptsPendingResourceLoad = [];
296 resource.addEventListener("finished", this._resourceLoadingFinished, this);
298 resource._scriptsPendingResourceLoad.push(script);
300 // Source frame content is outdated since we have new script parsed.
301 this._recreateSourceFrame(script.sourceURL);
303 } else if (!(script.sourceURL in this._sourceNameToFilesSelectOption)) {
304 // This is a dynamic script with "//@ sourceURL=" comment.
305 this._addOptionToFilesSelectAndShowSourceFrameIfNeeded(script.sourceURL);
309 _resourceForURL: function(url)
311 return WebInspector.networkManager.inflightResourceForURL(url) || WebInspector.resourceForURL(url);
314 _resourceLoadingFinished: function(e)
316 var resource = e.target;
318 // Bind scripts to resource.
319 for (var i = 0; i < resource._scriptsPendingResourceLoad.length; ++i) {
320 var script = resource._scriptsPendingResourceLoad[i];
321 script.resource = resource;
323 delete resource._scriptsPendingResourceLoad;
325 // Recreate source frame to show resource content.
326 this._recreateSourceFrame(resource.url);
328 // Add resource url to files select if not already added while debugging inlined scripts.
329 if (!(resource.url in this._sourceNameToFilesSelectOption))
330 this._addOptionToFilesSelectAndShowSourceFrameIfNeeded(resource.url);
333 _addOptionToFilesSelectAndShowSourceFrameIfNeeded: function(url)
335 this._addOptionToFilesSelect(url);
337 var lastViewedURL = WebInspector.settings.lastViewedScriptFile;
338 if (this._filesSelectElement.length === 1) {
339 // Option we just added is the only option in files select.
340 // We have to show corresponding source frame immediately.
341 this._showSourceFrameAndAddToHistory(url);
342 // Restore original value of lastViewedScriptFile because
343 // source frame was shown as a result of initial load.
344 WebInspector.settings.lastViewedScriptFile = lastViewedURL;
345 } else if (url === lastViewedURL)
346 this._showSourceFrameAndAddToHistory(url);
349 _addOptionToFilesSelect: function(sourceName)
351 var script = this._scriptForSourceName(sourceName);
352 var select = this._filesSelectElement;
353 var option = document.createElement("option");
354 option.text = script.sourceURL ? WebInspector.displayNameForURL(script.sourceURL) : WebInspector.UIString("(program)");
355 if (script.worldType === WebInspector.Script.WorldType.EXTENSIONS_WORLD)
356 option.addStyleClass("extension-script");
357 function optionCompare(a, b)
359 if (a.text === b.text)
361 return a.text < b.text ? -1 : 1;
363 var insertionIndex = insertionIndexForObjectInListSortedByFunction(option, select.childNodes, optionCompare);
364 if (insertionIndex < 0)
365 select.appendChild(option);
367 select.insertBefore(option, select.childNodes.item(insertionIndex));
369 option._sourceName = sourceName;
370 this._sourceNameToFilesSelectOption[sourceName] = option;
373 addConsoleMessage: function(message)
375 this._messages.push(message);
376 var sourceFrame = this._sourceNameToSourceFrame[message.url];
378 sourceFrame.addMessage(message);
381 clearConsoleMessages: function()
384 for (var url in this._sourceNameToSourceFrame)
385 this._sourceNameToSourceFrame[url].clearMessages();
388 _breakpointAdded: function(event)
390 var breakpoint = event.data;
392 var sourceFrame = this._sourceNameToSourceFrame[breakpoint.sourceName];
393 if (sourceFrame && sourceFrame.loaded)
394 sourceFrame.addBreakpoint(breakpoint.lineNumber, breakpoint.resolved, breakpoint.condition, breakpoint.enabled);
397 _breakpointRemoved: function(event)
399 var breakpoint = event.data;
401 var sourceFrame = this._sourceNameToSourceFrame[breakpoint.sourceName];
402 if (sourceFrame && sourceFrame.loaded)
403 sourceFrame.removeBreakpoint(breakpoint.lineNumber);
406 selectedCallFrameId: function()
408 var selectedCallFrame = this.sidebarPanes.callstack.selectedCallFrame;
409 if (!selectedCallFrame)
411 return selectedCallFrame.id;
414 evaluateInSelectedCallFrame: function(code, updateInterface, objectGroup, includeCommandLineAPI, callback)
416 var selectedCallFrame = this.sidebarPanes.callstack.selectedCallFrame;
417 if (!this._paused || !selectedCallFrame)
420 if (typeof updateInterface === "undefined")
421 updateInterface = true;
423 function updatingCallbackWrapper(result)
426 callback(WebInspector.RemoteObject.fromPayload(result));
428 this.sidebarPanes.scopechain.update(selectedCallFrame);
431 DebuggerAgent.evaluateOnCallFrame(selectedCallFrame.id, code, objectGroup, includeCommandLineAPI, updatingCallbackWrapper.bind(this));
434 _debuggerPaused: function(event)
436 var callFrames = event.data.callFrames;
439 this._waitingToPause = false;
440 this._stepping = false;
442 this._updateDebuggerButtons();
444 WebInspector.currentPanel = this;
446 this.sidebarPanes.callstack.update(event.data);
447 this.sidebarPanes.callstack.selectedCallFrame = callFrames[0];
450 InspectorFrontendHost.bringToFront();
453 _debuggerResumed: function()
455 this._paused = false;
456 this._waitingToPause = false;
457 this._stepping = false;
459 this._clearInterface();
462 debuggerWasEnabled: function()
464 this._setPauseOnExceptions(WebInspector.settings.pauseOnExceptionState);
466 if (this._debuggerEnabled)
468 this._debuggerEnabled = true;
472 debuggerWasDisabled: function()
474 if (!this._debuggerEnabled)
477 this._debuggerEnabled = false;
481 reset: function(preserveItems)
483 this.visibleView = null;
485 delete this.currentQuery;
486 this.searchCanceled();
488 this._debuggerResumed();
490 this._backForwardList = [];
491 this._currentBackForwardIndex = -1;
492 this._updateBackAndForwardButtons();
494 this._sourceNameToSourceFrame = {};
495 this._sourceNameToFilesSelectOption = {};
497 this._filesSelectElement.removeChildren();
498 this.functionsSelectElement.removeChildren();
499 this.viewsContainerElement.removeChildren();
501 this.sidebarPanes.watchExpressions.refreshExpressions();
503 this.sidebarPanes.workers.reset();
508 return this._visibleView;
513 if (this._visibleView === x)
516 if (this._visibleView)
517 this._visibleView.hide();
519 this._visibleView = x;
522 x.show(this.viewsContainerElement);
525 canShowSourceLine: function(url, line)
527 return this._debuggerEnabled && (url in this._sourceNameToFilesSelectOption);
530 showSourceLine: function(url, line)
532 if (!(url in this._sourceNameToFilesSelectOption))
534 var sourceFrame = this._showSourceFrameAndAddToHistory(url);
535 sourceFrame.highlightLine(line);
538 handleShortcut: function(event)
540 var shortcut = WebInspector.KeyboardShortcut.makeKeyFromEvent(event);
541 var handler = this._shortcuts[shortcut];
544 event.handled = true;
546 this.sidebarPanes.callstack.handleShortcut(event);
549 _showSourceFrameAndAddToHistory: function(sourceName)
551 var sourceFrame = this._showSourceFrame(sourceName);
553 var oldIndex = this._currentBackForwardIndex;
555 this._backForwardList.splice(oldIndex + 1, this._backForwardList.length - oldIndex);
557 // Check for a previous entry of the same object in _backForwardList.
558 // If one is found, remove it.
559 var previousEntryIndex = this._backForwardList.indexOf(sourceName);
560 if (previousEntryIndex !== -1)
561 this._backForwardList.splice(previousEntryIndex, 1);
563 this._backForwardList.push(sourceName);
564 this._currentBackForwardIndex = this._backForwardList.length - 1;
566 this._updateBackAndForwardButtons();
571 _showSourceFrame: function(sourceName)
573 var index = this._sourceNameToFilesSelectOption[sourceName].index;
574 this._filesSelectElement.selectedIndex = index;
576 var sourceFrame = this._sourceFrameForSourceName(sourceName);
577 this.visibleView = sourceFrame;
579 var script = this._scriptForSourceName(sourceName);
580 if (script.sourceURL)
581 WebInspector.settings.lastViewedScriptFile = script.sourceURL;
586 _sourceFrameForSourceName: function(sourceName)
588 var sourceFrame = this._sourceNameToSourceFrame[sourceName];
589 return sourceFrame || this._createSourceFrame(sourceName);
592 _createSourceFrame: function(sourceName)
594 var script = this._scriptForSourceName(sourceName);
597 if (script.resource) {
598 contentProvider = new WebInspector.SourceFrameContentProviderForResource(script.resource);
599 isScript = script.resource.type === WebInspector.Resource.Type.Script;
601 contentProvider = new WebInspector.SourceFrameContentProviderForScript(script);
602 isScript = !script.lineOffset && !script.columnOffset;
604 sourceFrame = new WebInspector.SourceFrame(contentProvider, script.sourceURL, isScript);
605 sourceFrame._sourceName = sourceName;
606 sourceFrame.addEventListener(WebInspector.SourceFrame.Events.Loaded, this._sourceFrameLoaded, this);
607 this._sourceNameToSourceFrame[sourceName] = sourceFrame;
611 _recreateSourceFrame: function(sourceName)
613 var oldSourceFrame = this._sourceNameToSourceFrame[sourceName];
616 oldSourceFrame.removeEventListener(WebInspector.SourceFrame.Events.Loaded, this._sourceFrameLoaded, this);
617 delete this._sourceNameToSourceFrame[sourceName];
618 oldSourceFrame.removeEventListener(WebInspector.SourceFrame.Events.Loaded, this._sourceFrameLoaded, this);
619 if (this.visibleView !== oldSourceFrame)
622 var newSourceFrame = this._createSourceFrame(sourceName)
623 newSourceFrame.scrollTop = oldSourceFrame.scrollTop;
624 this.visibleView = newSourceFrame;
627 _sourceFrameLoaded: function(event)
629 var sourceFrame = event.target;
630 var sourceName = sourceFrame._sourceName;
632 for (var i = 0; i < this._messages.length; ++i) {
633 var message = this._messages[i];
634 if (message.url === sourceName)
635 sourceFrame.addMessage(message);
638 var breakpoints = this._presentationModel.breakpointsForSourceName(sourceName);
639 for (var i = 0; i < breakpoints.length; ++i) {
640 var breakpoint = breakpoints[i];
641 sourceFrame.addBreakpoint(breakpoint.lineNumber, breakpoint.resolved, breakpoint.condition, breakpoint.enabled);
644 var selectedCallFrame = this.sidebarPanes.callstack.selectedCallFrame;
645 if (selectedCallFrame) {
646 var script = WebInspector.debuggerModel.scriptForSourceID(selectedCallFrame.sourceID);
647 if (this._sourceNameForScript(script) === sourceName) {
648 sourceFrame.setExecutionLine(selectedCallFrame.line);
649 this._executionSourceFrame = sourceFrame;
654 _sourceNameForScript: function(script)
656 return script.sourceURL || script.sourceID;
659 _scriptForSourceName: function(sourceName)
661 function filter(script)
663 return (script.sourceURL || script.sourceID) === sourceName;
665 return WebInspector.debuggerModel.queryScripts(filter)[0];
668 _clearCurrentExecutionLine: function()
670 if (this._executionSourceFrame)
671 this._executionSourceFrame.clearExecutionLine();
672 delete this._executionSourceFrame;
675 _callFrameSelected: function()
677 this._clearCurrentExecutionLine();
679 var callStackPane = this.sidebarPanes.callstack;
680 var currentFrame = callStackPane.selectedCallFrame;
684 this.sidebarPanes.scopechain.update(currentFrame);
685 this.sidebarPanes.watchExpressions.refreshExpressions();
687 var script = WebInspector.debuggerModel.scriptForSourceID(currentFrame.sourceID);
688 var sourceName = this._sourceNameForScript(script);
689 if (!(sourceName in this._sourceNameToFilesSelectOption)) {
690 // This happens in two cases:
691 // 1) Current call frame function is defined in anonymous script (anonymous scripts aren't added to files select by default)
692 // 2) We are debugging synchronously executed inlined script and there is no resource so far
693 this._addOptionToFilesSelect(sourceName);
695 var sourceFrame = this._showSourceFrameAndAddToHistory(sourceName);
696 if (sourceFrame.loaded) {
697 sourceFrame.setExecutionLine(currentFrame.line);
698 this._executionSourceFrame = sourceFrame;
702 _filesSelectChanged: function()
704 var sourceName = this._filesSelectElement[this._filesSelectElement.selectedIndex]._sourceName;
705 this._showSourceFrameAndAddToHistory(sourceName);
708 _startSidebarResizeDrag: function(event)
710 WebInspector.elementDragStart(this.sidebarElement, this._sidebarResizeDrag.bind(this), this._endSidebarResizeDrag.bind(this), event, "col-resize");
712 if (event.target === this.sidebarResizeWidgetElement)
713 this._dragOffset = (event.target.offsetWidth - (event.pageX - event.target.totalOffsetLeft));
715 this._dragOffset = 0;
718 _endSidebarResizeDrag: function(event)
720 WebInspector.elementDragEnd(event);
721 delete this._dragOffset;
722 this.saveSidebarWidth();
725 _sidebarResizeDrag: function(event)
727 var x = event.pageX + this._dragOffset;
728 var newWidth = Number.constrain(window.innerWidth - x, Preferences.minScriptsSidebarWidth, window.innerWidth * 0.66);
729 this.setSidebarWidth(newWidth);
730 event.preventDefault();
733 setSidebarWidth: function(newWidth)
735 this.sidebarElement.style.width = newWidth + "px";
736 this.sidebarButtonsElement.style.width = newWidth + "px";
737 this.viewsContainerElement.style.right = newWidth + "px";
738 this.sidebarResizeWidgetElement.style.right = newWidth + "px";
739 this.sidebarResizeElement.style.right = (newWidth - 3) + "px";
744 _setPauseOnExceptions: function(pauseOnExceptionsState)
746 function callback(pauseOnExceptionsState)
748 if (pauseOnExceptionsState == WebInspector.ScriptsPanel.PauseOnExceptionsState.DontPauseOnExceptions)
749 this._pauseOnExceptionButton.title = WebInspector.UIString("Don't pause on exceptions.\nClick to Pause on all exceptions.");
750 else if (pauseOnExceptionsState == WebInspector.ScriptsPanel.PauseOnExceptionsState.PauseOnAllExceptions)
751 this._pauseOnExceptionButton.title = WebInspector.UIString("Pause on all exceptions.\nClick to Pause on uncaught exceptions.");
752 else if (pauseOnExceptionsState == WebInspector.ScriptsPanel.PauseOnExceptionsState.PauseOnUncaughtExceptions)
753 this._pauseOnExceptionButton.title = WebInspector.UIString("Pause on uncaught exceptions.\nClick to Not pause on exceptions.");
755 this._pauseOnExceptionButton.state = pauseOnExceptionsState;
756 WebInspector.settings.pauseOnExceptionState = pauseOnExceptionsState;
758 DebuggerAgent.setPauseOnExceptionsState(pauseOnExceptionsState, callback.bind(this));
761 _updateDebuggerButtons: function()
763 if (this._debuggerEnabled) {
764 this.enableToggleButton.title = WebInspector.UIString("Debugging enabled. Click to disable.");
765 this.enableToggleButton.toggled = true;
766 this._pauseOnExceptionButton.visible = true;
767 this.panelEnablerView.visible = false;
769 this.enableToggleButton.title = WebInspector.UIString("Debugging disabled. Click to enable.");
770 this.enableToggleButton.toggled = false;
771 this._pauseOnExceptionButton.visible = false;
772 this.panelEnablerView.visible = true;
776 this.pauseButton.addStyleClass("paused");
778 this.pauseButton.disabled = false;
779 this.stepOverButton.disabled = false;
780 this.stepIntoButton.disabled = false;
781 this.stepOutButton.disabled = false;
783 this.debuggerStatusElement.textContent = WebInspector.UIString("Paused");
785 this.pauseButton.removeStyleClass("paused");
787 this.pauseButton.disabled = this._waitingToPause;
788 this.stepOverButton.disabled = true;
789 this.stepIntoButton.disabled = true;
790 this.stepOutButton.disabled = true;
792 if (this._waitingToPause)
793 this.debuggerStatusElement.textContent = WebInspector.UIString("Pausing");
794 else if (this._stepping)
795 this.debuggerStatusElement.textContent = WebInspector.UIString("Stepping");
797 this.debuggerStatusElement.textContent = "";
801 _updateBackAndForwardButtons: function()
803 this.backButton.disabled = this._currentBackForwardIndex <= 0;
804 this.forwardButton.disabled = this._currentBackForwardIndex >= (this._backForwardList.length - 1);
807 _clearInterface: function()
809 this.sidebarPanes.callstack.update(null);
810 this.sidebarPanes.scopechain.update(null);
812 this._clearCurrentExecutionLine();
813 this._updateDebuggerButtons();
818 if (this._currentBackForwardIndex <= 0) {
819 console.error("Can't go back from index " + this._currentBackForwardIndex);
823 this._showSourceFrame(this._backForwardList[--this._currentBackForwardIndex]);
824 this._updateBackAndForwardButtons();
827 _goForward: function()
829 if (this._currentBackForwardIndex >= this._backForwardList.length - 1) {
830 console.error("Can't go forward from index " + this._currentBackForwardIndex);
834 this._showSourceFrame(this._backForwardList[++this._currentBackForwardIndex]);
835 this._updateBackAndForwardButtons();
838 _formatScript: function()
840 if (this.visibleView)
841 this.visibleView.formatSource();
844 _enableDebugging: function()
846 if (this._debuggerEnabled)
848 this._toggleDebugging(this.panelEnablerView.alwaysEnabled);
851 _toggleDebugging: function(optionalAlways)
853 this._paused = false;
854 this._waitingToPause = false;
855 this._stepping = false;
857 if (this._debuggerEnabled) {
858 WebInspector.settings.debuggerEnabled = false;
859 WebInspector.debuggerModel.disableDebugger();
861 WebInspector.settings.debuggerEnabled = !!optionalAlways;
862 WebInspector.debuggerModel.enableDebugger();
866 _togglePauseOnExceptions: function()
868 this._setPauseOnExceptions((this._pauseOnExceptionButton.state + 1) % this._pauseOnExceptionButton.states);
871 _togglePause: function()
874 this._paused = false;
875 this._waitingToPause = false;
876 DebuggerAgent.resume();
878 this._stepping = false;
879 this._waitingToPause = true;
880 DebuggerAgent.pause();
883 this._clearInterface();
886 _stepOverClicked: function()
888 this._paused = false;
889 this._stepping = true;
891 this._clearInterface();
893 DebuggerAgent.stepOver();
896 _stepIntoClicked: function()
898 this._paused = false;
899 this._stepping = true;
901 this._clearInterface();
903 DebuggerAgent.stepInto();
906 _stepOutClicked: function()
908 this._paused = false;
909 this._stepping = true;
911 this._clearInterface();
913 DebuggerAgent.stepOut();
916 toggleBreakpointsClicked: function()
918 this.toggleBreakpointsButton.toggled = !this.toggleBreakpointsButton.toggled;
919 if (this.toggleBreakpointsButton.toggled) {
920 DebuggerAgent.activateBreakpoints();
921 this.toggleBreakpointsButton.title = WebInspector.UIString("Deactivate all breakpoints.");
922 document.getElementById("main-panels").removeStyleClass("breakpoints-deactivated");
924 DebuggerAgent.deactivateBreakpoints();
925 this.toggleBreakpointsButton.title = WebInspector.UIString("Activate all breakpoints.");
926 document.getElementById("main-panels").addStyleClass("breakpoints-deactivated");
930 elementsToRestoreScrollPositionsFor: function()
932 return [ this.sidebarElement ];
935 _registerShortcuts: function()
937 var section = WebInspector.shortcutsHelp.section(WebInspector.UIString("Scripts Panel"));
938 var handler, shortcut1, shortcut2;
939 var platformSpecificModifier = WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta;
941 this._shortcuts = {};
944 handler = this.pauseButton.click.bind(this.pauseButton);
945 shortcut1 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.F8);
946 this._shortcuts[shortcut1.key] = handler;
947 shortcut2 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Slash, platformSpecificModifier);
948 this._shortcuts[shortcut2.key] = handler;
949 section.addAlternateKeys([ shortcut1.name, shortcut2.name ], WebInspector.UIString("Continue"));
952 handler = this.stepOverButton.click.bind(this.stepOverButton);
953 shortcut1 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.F10);
954 this._shortcuts[shortcut1.key] = handler;
955 shortcut2 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.SingleQuote, platformSpecificModifier);
956 this._shortcuts[shortcut2.key] = handler;
957 section.addAlternateKeys([ shortcut1.name, shortcut2.name ], WebInspector.UIString("Step over"));
960 handler = this.stepIntoButton.click.bind(this.stepIntoButton);
961 shortcut1 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.F11);
962 this._shortcuts[shortcut1.key] = handler;
963 shortcut2 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Semicolon, platformSpecificModifier);
964 this._shortcuts[shortcut2.key] = handler;
965 section.addAlternateKeys([ shortcut1.name, shortcut2.name ], WebInspector.UIString("Step into"));
968 handler = this.stepOutButton.click.bind(this.stepOutButton);
969 shortcut1 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.F11, WebInspector.KeyboardShortcut.Modifiers.Shift);
970 this._shortcuts[shortcut1.key] = handler;
971 shortcut2 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Semicolon, WebInspector.KeyboardShortcut.Modifiers.Shift, platformSpecificModifier);
972 this._shortcuts[shortcut2.key] = handler;
973 section.addAlternateKeys([ shortcut1.name, shortcut2.name ], WebInspector.UIString("Step out"));
975 var isMac = WebInspector.isMac();
977 shortcut1 = WebInspector.KeyboardShortcut.makeDescriptor("l", WebInspector.KeyboardShortcut.Modifiers.Meta);
979 shortcut1 = WebInspector.KeyboardShortcut.makeDescriptor("g", WebInspector.KeyboardShortcut.Modifiers.Ctrl);
980 this._shortcuts[shortcut1.key] = this.showGoToLineDialog.bind(this);
981 section.addAlternateKeys([ shortcut1.name ], WebInspector.UIString("Go to Line"));
982 this.sidebarPanes.callstack.registerShortcuts(section);
985 searchCanceled: function()
987 if (this._searchView)
988 this._searchView.searchCanceled();
990 delete this._searchView;
991 delete this._searchQuery;
994 performSearch: function(query)
996 WebInspector.searchController.updateSearchMatchesCount(0, this);
998 if (!this.visibleView)
1001 // Call searchCanceled since it will reset everything we need before doing a new search.
1002 this.searchCanceled();
1004 this._searchView = this.visibleView;
1005 this._searchQuery = query;
1007 function finishedCallback(view, searchMatches)
1012 WebInspector.searchController.updateSearchMatchesCount(searchMatches, this);
1013 view.jumpToFirstSearchResult();
1016 this._searchView.performSearch(query, finishedCallback.bind(this));
1019 jumpToNextSearchResult: function()
1021 if (!this._searchView)
1024 if (this._searchView !== this.visibleView) {
1025 this.performSearch(this._searchQuery);
1029 if (this._searchView.showingLastSearchResult())
1030 this._searchView.jumpToFirstSearchResult();
1032 this._searchView.jumpToNextSearchResult();
1035 jumpToPreviousSearchResult: function()
1037 if (!this._searchView)
1040 if (this._searchView !== this.visibleView) {
1041 this.performSearch(this._searchQuery);
1042 if (this._searchView)
1043 this._searchView.jumpToLastSearchResult();
1047 if (this._searchView.showingFirstSearchResult())
1048 this._searchView.jumpToLastSearchResult();
1050 this._searchView.jumpToPreviousSearchResult();
1053 showGoToLineDialog: function(e)
1055 var view = this.visibleView;
1057 WebInspector.GoToLineDialog.show(view);
1061 WebInspector.ScriptsPanel.prototype.__proto__ = WebInspector.Panel.prototype;
1064 WebInspector.SourceFrameContentProviderForScript = function(script)
1066 WebInspector.SourceFrameContentProvider.call(this);
1067 this._script = script;
1070 WebInspector.SourceFrameContentProviderForScript.prototype = {
1071 requestContent: function(callback)
1073 var scripts = [this._script];
1074 if (this._script.sourceURL)
1075 scripts = WebInspector.debuggerModel.scriptsForURL(this._script.sourceURL);
1076 scripts.sort(function(x, y) { return x.lineOffset - y.lineOffset || x.columnOffset - y.columnOffset; });
1078 var scriptsLeft = scripts.length;
1080 function didRequestSource(index, source)
1082 sources[index] = source;
1085 var result = this._buildSource(scripts, sources);
1086 var sourceMapping = new WebInspector.IdenticalSourceMapping();
1087 callback(result.mimeType, new WebInspector.SourceFrameContent(result.source, sourceMapping, result.scriptRanges));
1090 for (var i = 0; i < scripts.length; ++i)
1091 scripts[i].requestSource(didRequestSource.bind(this, i));
1094 _buildSource: function(scripts, sources)
1098 var columnNumber = 0;
1099 var scriptRanges = [];
1100 function appendChunk(chunk, script)
1102 var start = { lineNumber: lineNumber, columnNumber: columnNumber };
1104 var lineEndings = chunk.lineEndings();
1105 var lineCount = lineEndings.length;
1106 if (lineCount === 1)
1107 columnNumber += chunk.length;
1109 lineNumber += lineCount - 1;
1110 columnNumber = lineEndings[lineCount - 1] - lineEndings[lineCount - 2] - 1;
1112 var end = { lineNumber: lineNumber, columnNumber: columnNumber };
1114 scriptRanges.push({ start: start, end: end, sourceID: script.sourceID });
1118 if (scripts.length === 1 && !scripts[0].lineOffset && !scripts[0].columnOffset) {
1119 // Single script source.
1120 mimeType = "text/javascript";
1121 appendChunk(sources[0], scripts[0]);
1123 // Scripts inlined in html document.
1124 mimeType = "text/html";
1125 var scriptOpenTag = "<script>";
1126 var scriptCloseTag = "</script>";
1127 for (var i = 0; i < scripts.length; ++i) {
1128 // Fill the gap with whitespace characters.
1129 while (lineNumber < scripts[i].lineOffset)
1131 while (columnNumber < scripts[i].columnOffset - scriptOpenTag.length)
1135 appendChunk(scriptOpenTag);
1136 appendChunk(sources[i], scripts[i]);
1137 appendChunk(scriptCloseTag);
1140 return { mimeType: mimeType, source: source, scriptRanges: scriptRanges };
1144 WebInspector.SourceFrameContentProviderForScript.prototype.__proto__ = WebInspector.SourceFrameContentProvider.prototype;