OSDN Git Service

Merge WebKit at r78450: Initial merge by git.
[android-x86/external-webkit.git] / Source / WebCore / inspector / front-end / ScriptsPanel.js
1 /*
2  * Copyright (C) 2008 Apple 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
6  * are met:
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.
12  *
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.
24  */
25
26 WebInspector.ScriptsPanel = function()
27 {
28     WebInspector.Panel.call(this, "scripts");
29
30     this.topStatusBar = document.createElement("div");
31     this.topStatusBar.className = "status-bar";
32     this.topStatusBar.id = "scripts-status-bar";
33     this.element.appendChild(this.topStatusBar);
34
35     this.backButton = document.createElement("button");
36     this.backButton.className = "status-bar-item";
37     this.backButton.id = "scripts-back";
38     this.backButton.title = WebInspector.UIString("Show the previous script resource.");
39     this.backButton.disabled = true;
40     this.backButton.appendChild(document.createElement("img"));
41     this.backButton.addEventListener("click", this._goBack.bind(this), false);
42     this.topStatusBar.appendChild(this.backButton);
43
44     this.forwardButton = document.createElement("button");
45     this.forwardButton.className = "status-bar-item";
46     this.forwardButton.id = "scripts-forward";
47     this.forwardButton.title = WebInspector.UIString("Show the next script resource.");
48     this.forwardButton.disabled = true;
49     this.forwardButton.appendChild(document.createElement("img"));
50     this.forwardButton.addEventListener("click", this._goForward.bind(this), false);
51     this.topStatusBar.appendChild(this.forwardButton);
52
53     this.filesSelectElement = document.createElement("select");
54     this.filesSelectElement.className = "status-bar-item";
55     this.filesSelectElement.id = "scripts-files";
56     this.filesSelectElement.addEventListener("change", this._changeVisibleFile.bind(this), false);
57     this.topStatusBar.appendChild(this.filesSelectElement);
58
59     this.functionsSelectElement = document.createElement("select");
60     this.functionsSelectElement.className = "status-bar-item";
61     this.functionsSelectElement.id = "scripts-functions";
62
63     // FIXME: append the functions select element to the top status bar when it is implemented.
64     // this.topStatusBar.appendChild(this.functionsSelectElement);
65
66     this.formatButton = document.createElement("button");
67     this.formatButton.className = "status-bar-item";
68     this.formatButton.id = "format-script";
69     this.formatButton.title = WebInspector.UIString("Format script.");
70     this.formatButton.appendChild(document.createElement("img"));
71     this.formatButton.addEventListener("click", this._formatScript.bind(this), false);
72     if (Preferences.debugMode)
73         this.topStatusBar.appendChild(this.formatButton);
74
75     this.sidebarButtonsElement = document.createElement("div");
76     this.sidebarButtonsElement.id = "scripts-sidebar-buttons";
77     this.topStatusBar.appendChild(this.sidebarButtonsElement);
78
79     this.pauseButton = document.createElement("button");
80     this.pauseButton.className = "status-bar-item";
81     this.pauseButton.id = "scripts-pause";
82     this.pauseButton.title = WebInspector.UIString("Pause script execution.");
83     this.pauseButton.disabled = true;
84     this.pauseButton.appendChild(document.createElement("img"));
85     this.pauseButton.addEventListener("click", this._togglePause.bind(this), false);
86     this.sidebarButtonsElement.appendChild(this.pauseButton);
87
88     this.stepOverButton = document.createElement("button");
89     this.stepOverButton.className = "status-bar-item";
90     this.stepOverButton.id = "scripts-step-over";
91     this.stepOverButton.title = WebInspector.UIString("Step over next function call.");
92     this.stepOverButton.disabled = true;
93     this.stepOverButton.addEventListener("click", this._stepOverClicked.bind(this), false);
94     this.stepOverButton.appendChild(document.createElement("img"));
95     this.sidebarButtonsElement.appendChild(this.stepOverButton);
96
97     this.stepIntoButton = document.createElement("button");
98     this.stepIntoButton.className = "status-bar-item";
99     this.stepIntoButton.id = "scripts-step-into";
100     this.stepIntoButton.title = WebInspector.UIString("Step into next function call.");
101     this.stepIntoButton.disabled = true;
102     this.stepIntoButton.addEventListener("click", this._stepIntoClicked.bind(this), false);
103     this.stepIntoButton.appendChild(document.createElement("img"));
104     this.sidebarButtonsElement.appendChild(this.stepIntoButton);
105
106     this.stepOutButton = document.createElement("button");
107     this.stepOutButton.className = "status-bar-item";
108     this.stepOutButton.id = "scripts-step-out";
109     this.stepOutButton.title = WebInspector.UIString("Step out of current function.");
110     this.stepOutButton.disabled = true;
111     this.stepOutButton.addEventListener("click", this._stepOutClicked.bind(this), false);
112     this.stepOutButton.appendChild(document.createElement("img"));
113     this.sidebarButtonsElement.appendChild(this.stepOutButton);
114
115     this.toggleBreakpointsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Deactivate all breakpoints."), "toggle-breakpoints");
116     this.toggleBreakpointsButton.toggled = true;
117     this.toggleBreakpointsButton.addEventListener("click", this.toggleBreakpointsClicked.bind(this), false);
118     this.sidebarButtonsElement.appendChild(this.toggleBreakpointsButton.element);
119
120     this.debuggerStatusElement = document.createElement("div");
121     this.debuggerStatusElement.id = "scripts-debugger-status";
122     this.sidebarButtonsElement.appendChild(this.debuggerStatusElement);
123
124     this.viewsContainerElement = document.createElement("div");
125     this.viewsContainerElement.id = "script-resource-views";
126
127     this.sidebarElement = document.createElement("div");
128     this.sidebarElement.id = "scripts-sidebar";
129
130     this.sidebarResizeElement = document.createElement("div");
131     this.sidebarResizeElement.className = "sidebar-resizer-vertical";
132     this.sidebarResizeElement.addEventListener("mousedown", this._startSidebarResizeDrag.bind(this), false);
133
134     this.sidebarResizeWidgetElement = document.createElement("div");
135     this.sidebarResizeWidgetElement.id = "scripts-sidebar-resizer-widget";
136     this.sidebarResizeWidgetElement.addEventListener("mousedown", this._startSidebarResizeDrag.bind(this), false);
137     this.topStatusBar.appendChild(this.sidebarResizeWidgetElement);
138
139     this.sidebarPanes = {};
140     this.sidebarPanes.watchExpressions = new WebInspector.WatchExpressionsSidebarPane();
141     this.sidebarPanes.callstack = new WebInspector.CallStackSidebarPane();
142     this.sidebarPanes.scopechain = new WebInspector.ScopeChainSidebarPane();
143     this.sidebarPanes.jsBreakpoints = new WebInspector.JavaScriptBreakpointsSidebarPane();
144     if (Preferences.nativeInstrumentationEnabled) {
145         this.sidebarPanes.domBreakpoints = WebInspector.createDOMBreakpointsSidebarPane();
146         this.sidebarPanes.xhrBreakpoints = WebInspector.createXHRBreakpointsSidebarPane();
147         this.sidebarPanes.eventListenerBreakpoints = new WebInspector.EventListenerBreakpointsSidebarPane();
148     }
149
150     this.sidebarPanes.workers = new WebInspector.WorkersSidebarPane();
151
152     for (var pane in this.sidebarPanes)
153         this.sidebarElement.appendChild(this.sidebarPanes[pane].element);
154
155     this.sidebarPanes.callstack.expanded = true;
156     this.sidebarPanes.callstack.addEventListener("call frame selected", this._callFrameSelected, this);
157
158     this.sidebarPanes.scopechain.expanded = true;
159     this.sidebarPanes.jsBreakpoints.expanded = true;
160
161     var panelEnablerHeading = WebInspector.UIString("You need to enable debugging before you can use the Scripts panel.");
162     var panelEnablerDisclaimer = WebInspector.UIString("Enabling debugging will make scripts run slower.");
163     var panelEnablerButton = WebInspector.UIString("Enable Debugging");
164
165     this.panelEnablerView = new WebInspector.PanelEnablerView("scripts", panelEnablerHeading, panelEnablerDisclaimer, panelEnablerButton);
166     this.panelEnablerView.addEventListener("enable clicked", this._enableDebugging, this);
167
168     this.element.appendChild(this.panelEnablerView.element);
169     this.element.appendChild(this.viewsContainerElement);
170     this.element.appendChild(this.sidebarElement);
171     this.element.appendChild(this.sidebarResizeElement);
172
173     this.enableToggleButton = new WebInspector.StatusBarButton("", "enable-toggle-status-bar-item");
174     this.enableToggleButton.addEventListener("click", this._toggleDebugging.bind(this), false);
175     if (Preferences.debuggerAlwaysEnabled)
176         this.enableToggleButton.element.addStyleClass("hidden");
177
178     this._pauseOnExceptionButton = new WebInspector.StatusBarButton("", "scripts-pause-on-exceptions-status-bar-item", 3);
179     this._pauseOnExceptionButton.addEventListener("click", this._togglePauseOnExceptions.bind(this), false);
180
181     this._registerShortcuts();
182
183     this._debuggerEnabled = Preferences.debuggerAlwaysEnabled;
184
185     this.reset();
186
187     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ParsedScriptSource, this._parsedScriptSource, this);
188     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.FailedToParseScriptSource, this._failedToParseScriptSource, this);
189     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ScriptSourceChanged, this._scriptSourceChanged, this);
190     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPaused, this);
191     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerResumed, this._debuggerResumed, this);
192 }
193
194 // Keep these in sync with WebCore::ScriptDebugServer
195 WebInspector.ScriptsPanel.PauseOnExceptionsState = {
196     DontPauseOnExceptions : 0,
197     PauseOnAllExceptions : 1,
198     PauseOnUncaughtExceptions: 2
199 };
200
201 WebInspector.ScriptsPanel.prototype = {
202     get toolbarItemLabel()
203     {
204         return WebInspector.UIString("Scripts");
205     },
206
207     get statusBarItems()
208     {
209         return [this.enableToggleButton.element, this._pauseOnExceptionButton.element];
210     },
211
212     get defaultFocusedElement()
213     {
214         return this.filesSelectElement;
215     },
216
217     get paused()
218     {
219         return this._paused;
220     },
221
222     show: function()
223     {
224         WebInspector.Panel.prototype.show.call(this);
225         this.sidebarResizeElement.style.right = (this.sidebarElement.offsetWidth - 3) + "px";
226
227         if (this.visibleView)
228             this.visibleView.show(this.viewsContainerElement);
229     },
230
231     hide: function()
232     {
233         if (this.visibleView)
234             this.visibleView.hide();
235         WebInspector.Panel.prototype.hide.call(this);
236     },
237
238     get breakpointsActivated()
239     {
240         return this.toggleBreakpointsButton.toggled;
241     },
242
243     _parsedScriptSource: function(event)
244     {
245         this._addScript(event.data);
246     },
247
248     _failedToParseScriptSource: function(event)
249     {
250         this._addScript(event.data);
251     },
252
253     _scriptSourceChanged: function(event)
254     {
255         var sourceID = event.data.sourceID;
256         var oldSource = event.data.oldSource;
257
258         var oldView, newView;
259         var script = WebInspector.debuggerModel.scriptForSourceID(sourceID);
260         if (script.resource) {
261             oldView = this._urlToSourceFrame[script.resource.url];
262             delete this._urlToSourceFrame[script.resource.url];
263             newView = this._sourceFrameForResource(script.resource);
264             var revertHandle = WebInspector.debuggerModel.editScriptSource.bind(WebInspector.debuggerModel, sourceID, oldSource);
265             script.resource.setContent(script.source, revertHandle);
266         } else {
267             var oldView = script._sourceFrame;
268             delete script._sourceFrame;
269             newView = this._sourceFrameForScript(script);
270         }
271         newView.scrollTop = oldView.scrollTop;
272
273         if (this.visibleView === oldView)
274             this.visibleView = newView;
275
276         var callFrames = WebInspector.debuggerModel.callFrames;
277         if (callFrames.length)
278             this._debuggerPaused({ data: { callFrames: callFrames } });
279     },
280
281     _addScript: function(script)
282     {
283         var resource = WebInspector.networkManager.inflightResourceForURL(script.sourceURL) || WebInspector.resourceForURL(script.sourceURL);
284         if (resource) {
285             if (resource.finished) {
286                 // Resource is finished, bind the script right away.
287                 script.resource = resource;
288             } else {
289                 // Resource is not finished, bind the script later.
290                 if (!resource._scriptsPendingResourceLoad) {
291                     resource._scriptsPendingResourceLoad = [];
292                     resource.addEventListener("finished", this._resourceLoadingFinished, this);
293                 }
294                 resource._scriptsPendingResourceLoad.push(script);
295             }
296         }
297         this._addScriptToFilesMenu(script);
298     },
299
300     _resourceLoadingFinished: function(e)
301     {
302         var resource = e.target;
303
304         var visible = false;
305         var select = this.filesSelectElement;
306         for (var i = 0; i < resource._scriptsPendingResourceLoad.length; ++i) {
307             // Bind script to resource.
308             var script = resource._scriptsPendingResourceLoad[i];
309             script.resource = resource;
310
311             if (select.options[select.selectedIndex] === script.filesSelectOption)
312                 visible = true;
313
314             // Remove script from the files list.
315             script.filesSelectOption.parentElement.removeChild(script.filesSelectOption);
316         }
317         // Adding first script will add resource.
318         this._addScriptToFilesMenu(resource._scriptsPendingResourceLoad[0]);
319         delete resource._scriptsPendingResourceLoad;
320
321         if (visible)
322             this._showScriptOrResource(resource, { initialLoad: true });
323     },
324
325     addConsoleMessage: function(message)
326     {
327         this._messages.push(message);
328         var sourceFrame = this._urlToSourceFrame[message.url];
329         if (sourceFrame)
330             sourceFrame.addMessage(message);
331     },
332
333     clearConsoleMessages: function()
334     {
335         this._messages = [];
336         for (var url in this._urlToSourceFrame)
337             this._urlToSourceFrame[url].clearMessages();
338     },
339
340     selectedCallFrameId: function()
341     {
342         var selectedCallFrame = this.sidebarPanes.callstack.selectedCallFrame;
343         if (!selectedCallFrame)
344             return null;
345         return selectedCallFrame.id;
346     },
347
348     evaluateInSelectedCallFrame: function(code, updateInterface, objectGroup, includeCommandLineAPI, callback)
349     {
350         var selectedCallFrame = this.sidebarPanes.callstack.selectedCallFrame;
351         if (!this._paused || !selectedCallFrame)
352             return;
353
354         if (typeof updateInterface === "undefined")
355             updateInterface = true;
356
357         function updatingCallbackWrapper(result)
358         {
359             if (result) {
360                 callback(WebInspector.RemoteObject.fromPayload(result));
361                 if (updateInterface)
362                     this.sidebarPanes.scopechain.update(selectedCallFrame);
363             }
364         }
365         InspectorBackend.evaluateOnCallFrame(selectedCallFrame.id, code, objectGroup, includeCommandLineAPI, updatingCallbackWrapper.bind(this));
366     },
367
368     _debuggerPaused: function(event)
369     {
370         var callFrames = event.data.callFrames;
371
372         this._paused = true;
373         this._waitingToPause = false;
374         this._stepping = false;
375
376         this._updateDebuggerButtons();
377
378         WebInspector.currentPanel = this;
379
380         this.sidebarPanes.callstack.update(callFrames, event.data.eventType, event.data.eventData);
381         this.sidebarPanes.callstack.selectedCallFrame = callFrames[0];
382
383         window.focus();
384         InspectorFrontendHost.bringToFront();
385     },
386
387     _debuggerResumed: function()
388     {
389         this._paused = false;
390         this._waitingToPause = false;
391         this._stepping = false;
392
393         this._clearInterface();
394     },
395
396     debuggerWasEnabled: function()
397     {
398         this._setPauseOnExceptions(WebInspector.settings.pauseOnExceptionState);
399
400         if (this._debuggerEnabled)
401             return;
402         this._debuggerEnabled = true;
403         this.reset(true);
404     },
405
406     debuggerWasDisabled: function()
407     {
408         if (!this._debuggerEnabled)
409             return;
410
411         this._debuggerEnabled = false;
412         this.reset(true);
413     },
414
415     reset: function(preserveItems)
416     {
417         this.visibleView = null;
418
419         delete this.currentQuery;
420         this.searchCanceled();
421
422         this._debuggerResumed();
423
424         this._backForwardList = [];
425         this._currentBackForwardIndex = -1;
426         this._updateBackAndForwardButtons();
427
428         this._urlToSourceFrame = {};
429         this._messages = [];
430         this._resourceForURLInFilesSelect = {};
431         this.filesSelectElement.removeChildren();
432         this.functionsSelectElement.removeChildren();
433         this.viewsContainerElement.removeChildren();
434
435         this.sidebarPanes.watchExpressions.refreshExpressions();
436         if (!preserveItems)
437             this.sidebarPanes.workers.reset();
438     },
439
440     get visibleView()
441     {
442         return this._visibleView;
443     },
444
445     set visibleView(x)
446     {
447         if (this._visibleView === x)
448             return;
449
450         if (this._visibleView)
451             this._visibleView.hide();
452
453         this._visibleView = x;
454
455         if (x)
456             x.show(this.viewsContainerElement);
457     },
458
459     canShowSourceLine: function(url, line)
460     {
461         if (!this._debuggerEnabled)
462             return false;
463         return !!this._scriptOrResourceForURLAndLine(url, line);
464     },
465
466     showSourceLine: function(url, line)
467     {
468         var scriptOrResource = this._scriptOrResourceForURLAndLine(url, line);
469         this._showScriptOrResource(scriptOrResource, {line: line, shouldHighlightLine: true});
470     },
471
472     _scriptOrResourceForURLAndLine: function(url, line)
473     {
474         var scripts = WebInspector.debuggerModel.scriptsForURL(url);
475         for (var i = 0; i < scripts.length; ++i) {
476             var script = scripts[i];
477             if (script.resource)
478                 return script.resource;
479             if (script.startingLine <= line && script.startingLine + script.linesCount > line)
480                 return script;
481         }
482         return null;
483     },
484
485     showView: function(view)
486     {
487         if (!view)
488             return;
489         this._showScriptOrResource(view.resource || view.script);
490     },
491
492     handleShortcut: function(event)
493     {
494         var shortcut = WebInspector.KeyboardShortcut.makeKeyFromEvent(event);
495         var handler = this._shortcuts[shortcut];
496         if (handler) {
497             handler(event);
498             event.handled = true;
499         } else
500             this.sidebarPanes.callstack.handleShortcut(event);
501     },
502
503     _sourceFrameForScriptOrResource: function(scriptOrResource)
504     {
505         if (scriptOrResource instanceof WebInspector.Resource)
506             return this._sourceFrameForResource(scriptOrResource);
507         return this._sourceFrameForScript(scriptOrResource);
508     },
509
510     _sourceFrameForResource: function(resource)
511     {
512         var sourceFrame = this._urlToSourceFrame[resource.url];
513         if (sourceFrame)
514             return sourceFrame;
515         var contentProvider = new WebInspector.SourceFrameContentProviderForResource(resource);
516         var isScript = resource.type === WebInspector.Resource.Type.Script;
517         sourceFrame = new WebInspector.SourceFrame(contentProvider, resource.url, isScript);
518         for (var i = 0; i < this._messages.length; ++i) {
519             var message = this._messages[i];
520             if (this._messages[i].url === resource.url)
521                 sourceFrame.addMessage(message);
522         }
523         this._urlToSourceFrame[resource.url] = sourceFrame;
524         return sourceFrame;
525     },
526
527     _sourceFrameForScript: function(script)
528     {
529         if (script._sourceFrame)
530             return script._sourceFrame;
531         var contentProvider = new WebInspector.SourceFrameContentProviderForScript(script);
532         script._sourceFrame = new WebInspector.SourceFrame(contentProvider, script.sourceURL, true);
533         return script._sourceFrame;
534     },
535
536     _showScriptOrResource: function(scriptOrResource, options)
537     {
538         // options = {line:, shouldHighlightLine:, fromBackForwardAction:, initialLoad:}
539         options = options || {};
540
541         if (!scriptOrResource)
542             return;
543
544         var view = this._sourceFrameForScriptOrResource(scriptOrResource);
545         if (!view)
546             return;
547
548         var url = scriptOrResource.url || scriptOrResource.sourceURL;
549         if (url && !options.initialLoad)
550             WebInspector.settings.lastViewedScriptFile = url;
551
552         if (!options.fromBackForwardAction) {
553             var oldIndex = this._currentBackForwardIndex;
554             if (oldIndex >= 0)
555                 this._backForwardList.splice(oldIndex + 1, this._backForwardList.length - oldIndex);
556
557             // Check for a previous entry of the same object in _backForwardList.
558             // If one is found, remove it and update _currentBackForwardIndex to match.
559             var previousEntryIndex = this._backForwardList.indexOf(scriptOrResource);
560             if (previousEntryIndex !== -1) {
561                 this._backForwardList.splice(previousEntryIndex, 1);
562                 --this._currentBackForwardIndex;
563             }
564
565             this._backForwardList.push(scriptOrResource);
566             ++this._currentBackForwardIndex;
567
568             this._updateBackAndForwardButtons();
569         }
570
571         this.visibleView = view;
572
573         if (options.line) {
574             if (view.revealLine)
575                 view.revealLine(options.line);
576             if (view.highlightLine && options.shouldHighlightLine)
577                 view.highlightLine(options.line);
578         }
579
580         var option;
581         if (scriptOrResource instanceof WebInspector.Script) {
582             option = scriptOrResource.filesSelectOption;
583
584             // hasn't been added yet - happens for stepping in evals,
585             // so use the force option to force the script into the menu.
586             if (!option) {
587                 this._addScriptToFilesMenu(scriptOrResource, true);
588                 option = scriptOrResource.filesSelectOption;
589             }
590
591             console.assert(option);
592         } else
593             option = scriptOrResource.filesSelectOption;
594
595         if (option)
596             this.filesSelectElement.selectedIndex = option.index;
597     },
598
599     _addScriptToFilesMenu: function(script, force)
600     {
601         if (!script.sourceURL && !force)
602             return;
603
604         if (script.resource) {
605             if (this._resourceForURLInFilesSelect[script.resource.url])
606                 return;
607             this._resourceForURLInFilesSelect[script.resource.url] = script.resource;
608         }
609
610         var displayName = script.sourceURL ? WebInspector.displayNameForURL(script.sourceURL) : WebInspector.UIString("(program)");
611
612         var select = this.filesSelectElement;
613         var option = document.createElement("option");
614         option.representedObject = script.resource || script;
615         option.url = displayName;
616         option.startingLine = script.startingLine;
617         option.text = script.resource || script.startingLine === 1 ? displayName : String.sprintf("%s:%d", displayName, script.startingLine);
618
619         function optionCompare(a, b)
620         {
621             if (a.url < b.url)
622                 return -1;
623             else if (a.url > b.url)
624                 return 1;
625
626             if (typeof a.startingLine !== "number")
627                 return -1;
628             if (typeof b.startingLine !== "number")
629                 return -1;
630             return a.startingLine - b.startingLine;
631         }
632
633         var insertionIndex = insertionIndexForObjectInListSortedByFunction(option, select.childNodes, optionCompare);
634         if (insertionIndex < 0)
635             select.appendChild(option);
636         else
637             select.insertBefore(option, select.childNodes.item(insertionIndex));
638
639         if (script.resource)
640             script.resource.filesSelectOption = option;
641         else
642             script.filesSelectOption = option;
643
644         if (select.options[select.selectedIndex] === option) {
645             // Call _showScriptOrResource if the option we just appended ended up being selected.
646             // This will happen for the first item added to the menu.
647             this._showScriptOrResource(option.representedObject, {initialLoad: true});
648         } else {
649             // If not first item, check to see if this was the last viewed
650             var url = option.representedObject.url || option.representedObject.sourceURL;
651             var lastURL = WebInspector.settings.lastViewedScriptFile;
652             if (url && url === lastURL) {
653                 // For resources containing multiple <script> tags, we first report them separately and
654                 // then glue them all together. They all share url and there is no need to show them all one
655                 // by one.
656                 var isResource = !!option.representedObject.url;
657                 if (isResource || !this.visibleView || !this.visibleView.script || this.visibleView.script.sourceURL !== url)
658                     this._showScriptOrResource(option.representedObject, {initialLoad: true});
659             }
660         }
661
662         if (script.worldType === WebInspector.Script.WorldType.EXTENSIONS_WORLD)
663             script.filesSelectOption.addStyleClass("extension-script");
664     },
665
666     _clearCurrentExecutionLine: function()
667     {
668         if (this._executionSourceFrame)
669             this._executionSourceFrame.clearExecutionLine();
670         delete this._executionSourceFrame;
671     },
672
673     _callFrameSelected: function()
674     {
675         this._clearCurrentExecutionLine();
676
677         var callStackPane = this.sidebarPanes.callstack;
678         var currentFrame = callStackPane.selectedCallFrame;
679         if (!currentFrame)
680             return;
681
682         this.sidebarPanes.scopechain.update(currentFrame);
683         this.sidebarPanes.watchExpressions.refreshExpressions();
684
685         var script = WebInspector.debuggerModel.scriptForSourceID(currentFrame.sourceID);
686         var scriptOrResource = script.resource || script;
687         this._showScriptOrResource(scriptOrResource, {line: currentFrame.line});
688
689         this._executionSourceFrame = this._sourceFrameForScriptOrResource(scriptOrResource);
690         if (this._executionSourceFrame)
691             this._executionSourceFrame.setExecutionLine(currentFrame.line);
692     },
693
694     _changeVisibleFile: function(event)
695     {
696         var select = this.filesSelectElement;
697         this._showScriptOrResource(select.options[select.selectedIndex].representedObject);
698     },
699
700     _startSidebarResizeDrag: function(event)
701     {
702         WebInspector.elementDragStart(this.sidebarElement, this._sidebarResizeDrag.bind(this), this._endSidebarResizeDrag.bind(this), event, "col-resize");
703
704         if (event.target === this.sidebarResizeWidgetElement)
705             this._dragOffset = (event.target.offsetWidth - (event.pageX - event.target.totalOffsetLeft));
706         else
707             this._dragOffset = 0;
708     },
709
710     _endSidebarResizeDrag: function(event)
711     {
712         WebInspector.elementDragEnd(event);
713         delete this._dragOffset;
714         this.saveSidebarWidth();
715     },
716
717     _sidebarResizeDrag: function(event)
718     {
719         var x = event.pageX + this._dragOffset;
720         var newWidth = Number.constrain(window.innerWidth - x, Preferences.minScriptsSidebarWidth, window.innerWidth * 0.66);
721         this.setSidebarWidth(newWidth);
722         event.preventDefault();
723     },
724
725     setSidebarWidth: function(newWidth)
726     {
727         this.sidebarElement.style.width = newWidth + "px";
728         this.sidebarButtonsElement.style.width = newWidth + "px";
729         this.viewsContainerElement.style.right = newWidth + "px";
730         this.sidebarResizeWidgetElement.style.right = newWidth + "px";
731         this.sidebarResizeElement.style.right = (newWidth - 3) + "px";
732
733         this.resize();
734     },
735
736     _setPauseOnExceptions: function(pauseOnExceptionsState)
737     {
738         function callback(pauseOnExceptionsState)
739         {
740             if (pauseOnExceptionsState == WebInspector.ScriptsPanel.PauseOnExceptionsState.DontPauseOnExceptions)
741                 this._pauseOnExceptionButton.title = WebInspector.UIString("Don't pause on exceptions.\nClick to Pause on all exceptions.");
742             else if (pauseOnExceptionsState == WebInspector.ScriptsPanel.PauseOnExceptionsState.PauseOnAllExceptions)
743                 this._pauseOnExceptionButton.title = WebInspector.UIString("Pause on all exceptions.\nClick to Pause on uncaught exceptions.");
744             else if (pauseOnExceptionsState == WebInspector.ScriptsPanel.PauseOnExceptionsState.PauseOnUncaughtExceptions)
745                 this._pauseOnExceptionButton.title = WebInspector.UIString("Pause on uncaught exceptions.\nClick to Not pause on exceptions.");
746
747             this._pauseOnExceptionButton.state = pauseOnExceptionsState;
748             WebInspector.settings.pauseOnExceptionState = pauseOnExceptionsState;
749         }
750         InspectorBackend.setPauseOnExceptionsState(pauseOnExceptionsState, callback.bind(this));
751     },
752
753     _updateDebuggerButtons: function()
754     {
755         if (this._debuggerEnabled) {
756             this.enableToggleButton.title = WebInspector.UIString("Debugging enabled. Click to disable.");
757             this.enableToggleButton.toggled = true;
758             this._pauseOnExceptionButton.visible = true;
759             this.panelEnablerView.visible = false;
760         } else {
761             this.enableToggleButton.title = WebInspector.UIString("Debugging disabled. Click to enable.");
762             this.enableToggleButton.toggled = false;
763             this._pauseOnExceptionButton.visible = false;
764             this.panelEnablerView.visible = true;
765         }
766
767         if (this._paused) {
768             this.pauseButton.addStyleClass("paused");
769
770             this.pauseButton.disabled = false;
771             this.stepOverButton.disabled = false;
772             this.stepIntoButton.disabled = false;
773             this.stepOutButton.disabled = false;
774
775             this.debuggerStatusElement.textContent = WebInspector.UIString("Paused");
776         } else {
777             this.pauseButton.removeStyleClass("paused");
778
779             this.pauseButton.disabled = this._waitingToPause;
780             this.stepOverButton.disabled = true;
781             this.stepIntoButton.disabled = true;
782             this.stepOutButton.disabled = true;
783
784             if (this._waitingToPause)
785                 this.debuggerStatusElement.textContent = WebInspector.UIString("Pausing");
786             else if (this._stepping)
787                 this.debuggerStatusElement.textContent = WebInspector.UIString("Stepping");
788             else
789                 this.debuggerStatusElement.textContent = "";
790         }
791     },
792
793     _updateBackAndForwardButtons: function()
794     {
795         this.backButton.disabled = this._currentBackForwardIndex <= 0;
796         this.forwardButton.disabled = this._currentBackForwardIndex >= (this._backForwardList.length - 1);
797     },
798
799     _clearInterface: function()
800     {
801         this.sidebarPanes.callstack.update(null);
802         this.sidebarPanes.scopechain.update(null);
803
804         this._clearCurrentExecutionLine();
805         this._updateDebuggerButtons();
806     },
807
808     _goBack: function()
809     {
810         if (this._currentBackForwardIndex <= 0) {
811             console.error("Can't go back from index " + this._currentBackForwardIndex);
812             return;
813         }
814
815         this._showScriptOrResource(this._backForwardList[--this._currentBackForwardIndex], {fromBackForwardAction: true});
816         this._updateBackAndForwardButtons();
817     },
818
819     _goForward: function()
820     {
821         if (this._currentBackForwardIndex >= this._backForwardList.length - 1) {
822             console.error("Can't go forward from index " + this._currentBackForwardIndex);
823             return;
824         }
825
826         this._showScriptOrResource(this._backForwardList[++this._currentBackForwardIndex], {fromBackForwardAction: true});
827         this._updateBackAndForwardButtons();
828     },
829
830     _formatScript: function()
831     {
832         if (this.visibleView)
833             this.visibleView.formatSource();
834     },
835
836     _enableDebugging: function()
837     {
838         if (this._debuggerEnabled)
839             return;
840         this._toggleDebugging(this.panelEnablerView.alwaysEnabled);
841     },
842
843     _toggleDebugging: function(optionalAlways)
844     {
845         this._paused = false;
846         this._waitingToPause = false;
847         this._stepping = false;
848
849         if (this._debuggerEnabled) {
850             WebInspector.settings.debuggerEnabled = false;
851             WebInspector.debuggerModel.disableDebugger();
852         } else {
853             WebInspector.settings.debuggerEnabled = !!optionalAlways;
854             WebInspector.debuggerModel.enableDebugger();
855         }
856     },
857
858     _togglePauseOnExceptions: function()
859     {
860         this._setPauseOnExceptions((this._pauseOnExceptionButton.state + 1) % this._pauseOnExceptionButton.states);
861     },
862
863     _togglePause: function()
864     {
865         if (this._paused) {
866             this._paused = false;
867             this._waitingToPause = false;
868             InspectorBackend.resume();
869         } else {
870             this._stepping = false;
871             this._waitingToPause = true;
872             InspectorBackend.pause();
873         }
874
875         this._clearInterface();
876     },
877
878     _stepOverClicked: function()
879     {
880         this._paused = false;
881         this._stepping = true;
882
883         this._clearInterface();
884
885         InspectorBackend.stepOver();
886     },
887
888     _stepIntoClicked: function()
889     {
890         this._paused = false;
891         this._stepping = true;
892
893         this._clearInterface();
894
895         InspectorBackend.stepInto();
896     },
897
898     _stepOutClicked: function()
899     {
900         this._paused = false;
901         this._stepping = true;
902
903         this._clearInterface();
904
905         InspectorBackend.stepOut();
906     },
907
908     toggleBreakpointsClicked: function()
909     {
910         this.toggleBreakpointsButton.toggled = !this.toggleBreakpointsButton.toggled;
911         if (this.toggleBreakpointsButton.toggled) {
912             InspectorBackend.activateBreakpoints();
913             this.toggleBreakpointsButton.title = WebInspector.UIString("Deactivate all breakpoints.");
914             document.getElementById("main-panels").removeStyleClass("breakpoints-deactivated");
915         } else {
916             InspectorBackend.deactivateBreakpoints();
917             this.toggleBreakpointsButton.title = WebInspector.UIString("Activate all breakpoints.");
918             document.getElementById("main-panels").addStyleClass("breakpoints-deactivated");
919         }
920     },
921
922     elementsToRestoreScrollPositionsFor: function()
923     {
924         return [ this.sidebarElement ];
925     },
926
927     _registerShortcuts: function()
928     {
929         var section = WebInspector.shortcutsHelp.section(WebInspector.UIString("Scripts Panel"));
930         var handler, shortcut1, shortcut2;
931         var platformSpecificModifier = WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta;
932
933         this._shortcuts = {};
934
935         // Continue.
936         handler = this.pauseButton.click.bind(this.pauseButton);
937         shortcut1 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.F8);
938         this._shortcuts[shortcut1.key] = handler;
939         shortcut2 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Slash, platformSpecificModifier);
940         this._shortcuts[shortcut2.key] = handler;
941         section.addAlternateKeys([ shortcut1.name, shortcut2.name ], WebInspector.UIString("Continue"));
942
943         // Step over.
944         handler = this.stepOverButton.click.bind(this.stepOverButton);
945         shortcut1 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.F10);
946         this._shortcuts[shortcut1.key] = handler;
947         shortcut2 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.SingleQuote, platformSpecificModifier);
948         this._shortcuts[shortcut2.key] = handler;
949         section.addAlternateKeys([ shortcut1.name, shortcut2.name ], WebInspector.UIString("Step over"));
950
951         // Step into.
952         handler = this.stepIntoButton.click.bind(this.stepIntoButton);
953         shortcut1 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.F11);
954         this._shortcuts[shortcut1.key] = handler;
955         shortcut2 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Semicolon, platformSpecificModifier);
956         this._shortcuts[shortcut2.key] = handler;
957         section.addAlternateKeys([ shortcut1.name, shortcut2.name ], WebInspector.UIString("Step into"));
958
959         // Step out.
960         handler = this.stepOutButton.click.bind(this.stepOutButton);
961         shortcut1 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.F11, WebInspector.KeyboardShortcut.Modifiers.Shift);
962         this._shortcuts[shortcut1.key] = handler;
963         shortcut2 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Semicolon, WebInspector.KeyboardShortcut.Modifiers.Shift, platformSpecificModifier);
964         this._shortcuts[shortcut2.key] = handler;
965         section.addAlternateKeys([ shortcut1.name, shortcut2.name ], WebInspector.UIString("Step out"));
966
967         var isMac = WebInspector.isMac();
968         if (isMac)
969             shortcut1 = WebInspector.KeyboardShortcut.makeDescriptor("l", WebInspector.KeyboardShortcut.Modifiers.Meta);
970         else
971             shortcut1 = WebInspector.KeyboardShortcut.makeDescriptor("g", WebInspector.KeyboardShortcut.Modifiers.Ctrl);
972         this._shortcuts[shortcut1.key] = this.showGoToLineDialog.bind(this);
973         section.addAlternateKeys([ shortcut1.name ], WebInspector.UIString("Go to Line"));
974         this.sidebarPanes.callstack.registerShortcuts(section);
975     },
976
977     searchCanceled: function()
978     {
979         WebInspector.updateSearchMatchesCount(0, this);
980
981         if (this._searchView)
982             this._searchView.searchCanceled();
983
984         delete this._searchView;
985         delete this._searchQuery;
986     },
987
988     performSearch: function(query)
989     {
990         if (!this.visibleView)
991             return;
992
993         // Call searchCanceled since it will reset everything we need before doing a new search.
994         this.searchCanceled();
995
996         this._searchView = this.visibleView;
997         this._searchQuery = query;
998
999         function finishedCallback(view, searchMatches)
1000         {
1001             if (!searchMatches)
1002                 return;
1003
1004             WebInspector.updateSearchMatchesCount(searchMatches, this);
1005             view.jumpToFirstSearchResult();
1006         }
1007
1008         this._searchView.performSearch(query, finishedCallback.bind(this));
1009     },
1010
1011     jumpToNextSearchResult: function()
1012     {
1013         if (!this._searchView)
1014             return;
1015
1016         if (this._searchView !== this.visibleView) {
1017             this.performSearch(this._searchQuery);
1018             return;
1019         }
1020
1021         if (this._searchView.showingLastSearchResult())
1022             this._searchView.jumpToFirstSearchResult();
1023         else
1024             this._searchView.jumpToNextSearchResult();
1025     },
1026
1027     jumpToPreviousSearchResult: function()
1028     {
1029         if (!this._searchView)
1030             return;
1031
1032         if (this._searchView !== this.visibleView) {
1033             this.performSearch(this._searchQuery);
1034             if (this._searchView)
1035                 this._searchView.jumpToLastSearchResult();
1036             return;
1037         }
1038
1039         if (this._searchView.showingFirstSearchResult())
1040             this._searchView.jumpToLastSearchResult();
1041         else
1042             this._searchView.jumpToPreviousSearchResult();
1043     },
1044
1045     showGoToLineDialog: function(e)
1046     {
1047          var view = this.visibleView;
1048          if (view)
1049              WebInspector.GoToLineDialog.show(view);
1050     }
1051 }
1052
1053 WebInspector.ScriptsPanel.prototype.__proto__ = WebInspector.Panel.prototype;
1054
1055
1056 WebInspector.SourceFrameContentProviderForScript = function(script)
1057 {
1058     WebInspector.SourceFrameContentProvider.call(this);
1059     this._script = script;
1060 }
1061
1062 WebInspector.SourceFrameContentProviderForScript.prototype = {
1063     requestContent: function(callback)
1064     {
1065         if (this._script.source) {
1066             callback("text/javascript", this._script.source);
1067             return;
1068         }
1069
1070         function didRequestSource(content)
1071         {
1072             var source;
1073             if (content) {
1074                 var prefix = "";
1075                 for (var i = 0; i < this._script.startingLine - 1; ++i)
1076                     prefix += "\n";
1077                 source = prefix + content;
1078             } else
1079                 source = WebInspector.UIString("<source is not available>");
1080             callback("text/javascript", source);
1081         }
1082         this._script.requestSource(didRequestSource.bind(this));
1083     },
1084
1085     scripts: function()
1086     {
1087         return [this._script];
1088     }
1089 }
1090
1091 WebInspector.SourceFrameContentProviderForScript.prototype.__proto__ = WebInspector.SourceFrameContentProvider.prototype;