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 26WebInspector.ScriptsPanel = function() 27{ 28 WebInspector.Panel.call(this); 29 30 this.element.addStyleClass("scripts"); 31 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); 36 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); 45 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); 54 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._changeVisibleFile.bind(this), false); 59 this.filesSelectElement.handleKeyEvent = this.handleKeyEvent.bind(this); 60 this.topStatusBar.appendChild(this.filesSelectElement); 61 62 this.functionsSelectElement = document.createElement("select"); 63 this.functionsSelectElement.className = "status-bar-item"; 64 this.functionsSelectElement.id = "scripts-functions"; 65 66 // FIXME: append the functions select element to the top status bar when it is implemented. 67 // this.topStatusBar.appendChild(this.functionsSelectElement); 68 69 this.sidebarButtonsElement = document.createElement("div"); 70 this.sidebarButtonsElement.id = "scripts-sidebar-buttons"; 71 this.topStatusBar.appendChild(this.sidebarButtonsElement); 72 73 this.pauseButton = document.createElement("button"); 74 this.pauseButton.className = "status-bar-item"; 75 this.pauseButton.id = "scripts-pause"; 76 this.pauseButton.title = WebInspector.UIString("Pause script execution."); 77 this.pauseButton.disabled = true; 78 this.pauseButton.appendChild(document.createElement("img")); 79 this.pauseButton.addEventListener("click", this._togglePause.bind(this), false); 80 this.sidebarButtonsElement.appendChild(this.pauseButton); 81 82 this.stepOverButton = document.createElement("button"); 83 this.stepOverButton.className = "status-bar-item"; 84 this.stepOverButton.id = "scripts-step-over"; 85 this.stepOverButton.title = WebInspector.UIString("Step over next function call."); 86 this.stepOverButton.disabled = true; 87 this.stepOverButton.addEventListener("click", this._stepOverClicked.bind(this), false); 88 this.stepOverButton.appendChild(document.createElement("img")); 89 this.sidebarButtonsElement.appendChild(this.stepOverButton); 90 91 this.stepIntoButton = document.createElement("button"); 92 this.stepIntoButton.className = "status-bar-item"; 93 this.stepIntoButton.id = "scripts-step-into"; 94 this.stepIntoButton.title = WebInspector.UIString("Step into next function call."); 95 this.stepIntoButton.disabled = true; 96 this.stepIntoButton.addEventListener("click", this._stepIntoClicked.bind(this), false); 97 this.stepIntoButton.appendChild(document.createElement("img")); 98 this.sidebarButtonsElement.appendChild(this.stepIntoButton); 99 100 this.stepOutButton = document.createElement("button"); 101 this.stepOutButton.className = "status-bar-item"; 102 this.stepOutButton.id = "scripts-step-out"; 103 this.stepOutButton.title = WebInspector.UIString("Step out of current function."); 104 this.stepOutButton.disabled = true; 105 this.stepOutButton.addEventListener("click", this._stepOutClicked.bind(this), false); 106 this.stepOutButton.appendChild(document.createElement("img")); 107 this.sidebarButtonsElement.appendChild(this.stepOutButton); 108 109 this.debuggerStatusElement = document.createElement("div"); 110 this.debuggerStatusElement.id = "scripts-debugger-status"; 111 this.sidebarButtonsElement.appendChild(this.debuggerStatusElement); 112 113 this.viewsContainerElement = document.createElement("div"); 114 this.viewsContainerElement.id = "script-resource-views"; 115 116 this.sidebarElement = document.createElement("div"); 117 this.sidebarElement.id = "scripts-sidebar"; 118 119 this.sidebarResizeElement = document.createElement("div"); 120 this.sidebarResizeElement.className = "sidebar-resizer-vertical"; 121 this.sidebarResizeElement.addEventListener("mousedown", this._startSidebarResizeDrag.bind(this), false); 122 123 this.sidebarResizeWidgetElement = document.createElement("div"); 124 this.sidebarResizeWidgetElement.id = "scripts-sidebar-resizer-widget"; 125 this.sidebarResizeWidgetElement.addEventListener("mousedown", this._startSidebarResizeDrag.bind(this), false); 126 this.topStatusBar.appendChild(this.sidebarResizeWidgetElement); 127 128 this.sidebarPanes = {}; 129 this.sidebarPanes.callstack = new WebInspector.CallStackSidebarPane(); 130 this.sidebarPanes.scopechain = new WebInspector.ScopeChainSidebarPane(); 131 this.sidebarPanes.breakpoints = new WebInspector.BreakpointsSidebarPane(); 132 133 for (var pane in this.sidebarPanes) 134 this.sidebarElement.appendChild(this.sidebarPanes[pane].element); 135 136 this.sidebarPanes.callstack.expanded = true; 137 this.sidebarPanes.callstack.addEventListener("call frame selected", this._callFrameSelected, this); 138 139 this.sidebarPanes.scopechain.expanded = true; 140 this.sidebarPanes.breakpoints.expanded = true; 141 142 var panelEnablerHeading = WebInspector.UIString("You need to enable debugging before you can use the Scripts panel."); 143 var panelEnablerDisclaimer = WebInspector.UIString("Enabling debugging will make scripts run slower."); 144 var panelEnablerButton = WebInspector.UIString("Enable Debugging"); 145 146 this.panelEnablerView = new WebInspector.PanelEnablerView("scripts", panelEnablerHeading, panelEnablerDisclaimer, panelEnablerButton); 147 this.panelEnablerView.addEventListener("enable clicked", this._enableDebugging, this); 148 149 this.element.appendChild(this.panelEnablerView.element); 150 this.element.appendChild(this.viewsContainerElement); 151 this.element.appendChild(this.sidebarElement); 152 this.element.appendChild(this.sidebarResizeElement); 153 154 this.enableToggleButton = this.createStatusBarButton(); 155 this.enableToggleButton.className = "enable-toggle-status-bar-item status-bar-item"; 156 this.enableToggleButton.addEventListener("click", this._toggleDebugging.bind(this), false); 157 158 this.pauseOnExceptionButton = this.createStatusBarButton(); 159 this.pauseOnExceptionButton.id = "scripts-pause-on-exceptions-status-bar-item"; 160 this.pauseOnExceptionButton.className = "status-bar-item"; 161 this.pauseOnExceptionButton.addEventListener("click", this._togglePauseOnExceptions.bind(this), false); 162 163 this._breakpointsURLMap = {}; 164 165 this._shortcuts = {}; 166 167 var isMac = InspectorController.platform().indexOf("mac-") === 0; 168 var platformSpecificModifier = isMac ? WebInspector.KeyboardShortcut.Modifiers.Meta : WebInspector.KeyboardShortcut.Modifiers.Ctrl; 169 170 // Continue. 171 var handler = this.pauseButton.click.bind(this.pauseButton); 172 var shortcut = WebInspector.KeyboardShortcut.makeKey(WebInspector.KeyboardShortcut.KeyCodes.F8); 173 this._shortcuts[shortcut] = handler; 174 var shortcut = WebInspector.KeyboardShortcut.makeKey(WebInspector.KeyboardShortcut.KeyCodes.Slash, platformSpecificModifier); 175 this._shortcuts[shortcut] = handler; 176 177 // Step over. 178 var handler = this.stepOverButton.click.bind(this.stepOverButton); 179 var shortcut = WebInspector.KeyboardShortcut.makeKey(WebInspector.KeyboardShortcut.KeyCodes.F10); 180 this._shortcuts[shortcut] = handler; 181 var shortcut = WebInspector.KeyboardShortcut.makeKey(WebInspector.KeyboardShortcut.KeyCodes.SingleQuote, platformSpecificModifier); 182 this._shortcuts[shortcut] = handler; 183 184 // Step into. 185 var handler = this.stepIntoButton.click.bind(this.stepIntoButton); 186 var shortcut = WebInspector.KeyboardShortcut.makeKey(WebInspector.KeyboardShortcut.KeyCodes.F11); 187 this._shortcuts[shortcut] = handler; 188 var shortcut = WebInspector.KeyboardShortcut.makeKey(WebInspector.KeyboardShortcut.KeyCodes.Semicolon, platformSpecificModifier); 189 this._shortcuts[shortcut] = handler; 190 191 // Step out. 192 var handler = this.stepOutButton.click.bind(this.stepOutButton); 193 var shortcut = WebInspector.KeyboardShortcut.makeKey(WebInspector.KeyboardShortcut.KeyCodes.F11, WebInspector.KeyboardShortcut.Modifiers.Shift); 194 this._shortcuts[shortcut] = handler; 195 var shortcut = WebInspector.KeyboardShortcut.makeKey(WebInspector.KeyboardShortcut.KeyCodes.Semicolon, WebInspector.KeyboardShortcut.Modifiers.Shift, platformSpecificModifier); 196 this._shortcuts[shortcut] = handler; 197 198 this.reset(); 199} 200 201WebInspector.ScriptsPanel.prototype = { 202 toolbarItemClass: "scripts", 203 204 get toolbarItemLabel() 205 { 206 return WebInspector.UIString("Scripts"); 207 }, 208 209 get statusBarItems() 210 { 211 return [this.enableToggleButton, this.pauseOnExceptionButton]; 212 }, 213 214 get paused() 215 { 216 return this._paused; 217 }, 218 219 show: function() 220 { 221 WebInspector.Panel.prototype.show.call(this); 222 this.sidebarResizeElement.style.right = (this.sidebarElement.offsetWidth - 3) + "px"; 223 224 if (this.visibleView) { 225 if (this.visibleView instanceof WebInspector.ResourceView) 226 this.visibleView.headersVisible = false; 227 this.visibleView.show(this.viewsContainerElement); 228 } 229 230 // Hide any views that are visible that are not this panel's current visible view. 231 // This can happen when a ResourceView is visible in the Resources panel then switched 232 // to the this panel. 233 for (var sourceID in this._sourceIDMap) { 234 var scriptOrResource = this._sourceIDMap[sourceID]; 235 var view = this._sourceViewForScriptOrResource(scriptOrResource); 236 if (!view || view === this.visibleView) 237 continue; 238 view.visible = false; 239 } 240 if (this._attachDebuggerWhenShown) { 241 InspectorController.enableDebugger(false); 242 delete this._attachDebuggerWhenShown; 243 } 244 }, 245 246 get searchableViews() 247 { 248 var views = []; 249 250 const visibleView = this.visibleView; 251 if (visibleView && visibleView.performSearch) { 252 visibleView.alreadySearching = true; 253 views.push(visibleView); 254 } 255 256 for (var sourceID in this._sourceIDMap) { 257 var scriptOrResource = this._sourceIDMap[sourceID]; 258 var view = this._sourceViewForScriptOrResource(scriptOrResource); 259 if (!view || !view.performSearch || view.alreadySearching) 260 continue; 261 262 view.alreadySearching = true; 263 views.push(view); 264 } 265 266 for (var i = 0; i < views.length; ++i) 267 delete views[i].alreadySearching; 268 269 return views; 270 }, 271 272 addScript: function(sourceID, sourceURL, source, startingLine, errorLine, errorMessage) 273 { 274 var script = new WebInspector.Script(sourceID, sourceURL, source, startingLine, errorLine, errorMessage); 275 276 if (sourceURL in WebInspector.resourceURLMap) { 277 var resource = WebInspector.resourceURLMap[sourceURL]; 278 resource.addScript(script); 279 } 280 281 if (sourceURL in this._breakpointsURLMap && sourceID) { 282 var breakpoints = this._breakpointsURLMap[sourceURL]; 283 var breakpointsLength = breakpoints.length; 284 for (var i = 0; i < breakpointsLength; ++i) { 285 var breakpoint = breakpoints[i]; 286 if (startingLine <= breakpoint.line) { 287 breakpoint.sourceID = sourceID; 288 if (breakpoint.enabled) 289 InspectorController.addBreakpoint(breakpoint.sourceID, breakpoint.line); 290 } 291 } 292 } 293 294 if (sourceID) 295 this._sourceIDMap[sourceID] = (resource || script); 296 297 this._addScriptToFilesMenu(script); 298 }, 299 300 scriptOrResourceForID: function(id) 301 { 302 return this._sourceIDMap[id]; 303 }, 304 305 addBreakpoint: function(breakpoint) 306 { 307 this.sidebarPanes.breakpoints.addBreakpoint(breakpoint); 308 309 var sourceFrame; 310 if (breakpoint.url) { 311 if (!(breakpoint.url in this._breakpointsURLMap)) 312 this._breakpointsURLMap[breakpoint.url] = []; 313 this._breakpointsURLMap[breakpoint.url].unshift(breakpoint); 314 315 if (breakpoint.url in WebInspector.resourceURLMap) { 316 var resource = WebInspector.resourceURLMap[breakpoint.url]; 317 sourceFrame = this._sourceFrameForScriptOrResource(resource); 318 } 319 } 320 321 if (breakpoint.sourceID && !sourceFrame) { 322 var object = this._sourceIDMap[breakpoint.sourceID] 323 sourceFrame = this._sourceFrameForScriptOrResource(object); 324 } 325 326 if (sourceFrame) 327 sourceFrame.addBreakpoint(breakpoint); 328 }, 329 330 removeBreakpoint: function(breakpoint) 331 { 332 this.sidebarPanes.breakpoints.removeBreakpoint(breakpoint); 333 334 var sourceFrame; 335 if (breakpoint.url && breakpoint.url in this._breakpointsURLMap) { 336 var breakpoints = this._breakpointsURLMap[breakpoint.url]; 337 breakpoints.remove(breakpoint); 338 if (!breakpoints.length) 339 delete this._breakpointsURLMap[breakpoint.url]; 340 341 if (breakpoint.url in WebInspector.resourceURLMap) { 342 var resource = WebInspector.resourceURLMap[breakpoint.url]; 343 sourceFrame = this._sourceFrameForScriptOrResource(resource); 344 } 345 } 346 347 if (breakpoint.sourceID && !sourceFrame) { 348 var object = this._sourceIDMap[breakpoint.sourceID] 349 sourceFrame = this._sourceFrameForScriptOrResource(object); 350 } 351 352 if (sourceFrame) 353 sourceFrame.removeBreakpoint(breakpoint); 354 }, 355 356 evaluateInSelectedCallFrame: function(code, updateInterface, callback) 357 { 358 var selectedCallFrame = this.sidebarPanes.callstack.selectedCallFrame; 359 if (!this._paused || !selectedCallFrame) 360 return; 361 362 if (typeof updateInterface === "undefined") 363 updateInterface = true; 364 365 var self = this; 366 function updatingCallbackWrapper(result, exception) 367 { 368 callback(result, exception); 369 if (updateInterface) 370 self.sidebarPanes.scopechain.update(selectedCallFrame); 371 } 372 this.doEvalInCallFrame(selectedCallFrame, code, updatingCallbackWrapper); 373 }, 374 375 doEvalInCallFrame: function(callFrame, code, callback) 376 { 377 var panel = this; 378 function delayedEvaluation() 379 { 380 if (!code) { 381 // Evaluate into properties in scope of the selected call frame. 382 callback(panel._variablesInScope(callFrame)); 383 return; 384 } 385 try { 386 callback(callFrame.evaluate(code)); 387 } catch (e) { 388 callback(e, true); 389 } 390 } 391 setTimeout(delayedEvaluation, 0); 392 }, 393 394 _variablesInScope: function(callFrame) 395 { 396 var result = {}; 397 var scopeChain = callFrame.scopeChain; 398 for (var i = 0; i < scopeChain.length; ++i) { 399 var scopeObject = scopeChain[i]; 400 for (var property in scopeObject) 401 result[property] = true; 402 } 403 return result; 404 }, 405 406 debuggerPaused: function() 407 { 408 this._paused = true; 409 this._waitingToPause = false; 410 this._stepping = false; 411 412 this._updateDebuggerButtons(); 413 414 var callStackPane = this.sidebarPanes.callstack; 415 var currentFrame = InspectorController.currentCallFrame(); 416 callStackPane.update(currentFrame, this._sourceIDMap); 417 callStackPane.selectedCallFrame = currentFrame; 418 419 WebInspector.currentPanel = this; 420 window.focus(); 421 }, 422 423 debuggerResumed: function() 424 { 425 this._paused = false; 426 this._waitingToPause = false; 427 this._stepping = false; 428 429 this._clearInterface(); 430 }, 431 432 attachDebuggerWhenShown: function() 433 { 434 if (this.element.parentElement) { 435 InspectorController.enableDebugger(false); 436 } else { 437 this._attachDebuggerWhenShown = true; 438 } 439 }, 440 441 debuggerWasEnabled: function() 442 { 443 this.reset(); 444 }, 445 446 debuggerWasDisabled: function() 447 { 448 this.reset(); 449 }, 450 451 reset: function() 452 { 453 this.visibleView = null; 454 455 delete this.currentQuery; 456 this.searchCanceled(); 457 458 if (!InspectorController.debuggerEnabled()) { 459 this._paused = false; 460 this._waitingToPause = false; 461 this._stepping = false; 462 } 463 464 this._clearInterface(); 465 466 this._backForwardList = []; 467 this._currentBackForwardIndex = -1; 468 this._updateBackAndForwardButtons(); 469 470 this._scriptsForURLsInFilesSelect = {}; 471 this.filesSelectElement.removeChildren(); 472 this.functionsSelectElement.removeChildren(); 473 this.viewsContainerElement.removeChildren(); 474 475 if (this._sourceIDMap) { 476 for (var sourceID in this._sourceIDMap) { 477 var object = this._sourceIDMap[sourceID]; 478 if (object instanceof WebInspector.Resource) 479 object.removeAllScripts(); 480 } 481 } 482 483 this._sourceIDMap = {}; 484 }, 485 486 get visibleView() 487 { 488 return this._visibleView; 489 }, 490 491 set visibleView(x) 492 { 493 if (this._visibleView === x) 494 return; 495 496 if (this._visibleView) 497 this._visibleView.hide(); 498 499 this._visibleView = x; 500 501 if (x) 502 x.show(this.viewsContainerElement); 503 }, 504 505 canShowResource: function(resource) 506 { 507 return resource && resource.scripts.length && InspectorController.debuggerEnabled(); 508 }, 509 510 showScript: function(script, line) 511 { 512 this._showScriptOrResource(script, line, true); 513 }, 514 515 showResource: function(resource, line) 516 { 517 this._showScriptOrResource(resource, line, true); 518 }, 519 520 showView: function(view) 521 { 522 if (!view) 523 return; 524 this._showScriptOrResource((view.resource || view.script)); 525 }, 526 527 handleKeyEvent: function(event) 528 { 529 var shortcut = WebInspector.KeyboardShortcut.makeKeyFromEvent(event); 530 var handler = this._shortcuts[shortcut]; 531 if (handler) { 532 handler(event); 533 event.preventDefault(); 534 event.handled = true; 535 } else { 536 this.sidebarPanes.callstack.handleKeyEvent(event); 537 } 538 }, 539 540 scriptViewForScript: function(script) 541 { 542 if (!script) 543 return null; 544 if (!script._scriptView) 545 script._scriptView = new WebInspector.ScriptView(script); 546 return script._scriptView; 547 }, 548 549 sourceFrameForScript: function(script) 550 { 551 var view = this.scriptViewForScript(script); 552 if (!view) 553 return null; 554 555 // Setting up the source frame requires that we be attached. 556 if (!this.element.parentNode) 557 this.attach(); 558 559 view.setupSourceFrameIfNeeded(); 560 return view.sourceFrame; 561 }, 562 563 _sourceViewForScriptOrResource: function(scriptOrResource) 564 { 565 if (scriptOrResource instanceof WebInspector.Resource) { 566 if (!WebInspector.panels.resources) 567 return null; 568 return WebInspector.panels.resources.resourceViewForResource(scriptOrResource); 569 } 570 if (scriptOrResource instanceof WebInspector.Script) 571 return this.scriptViewForScript(scriptOrResource); 572 }, 573 574 _sourceFrameForScriptOrResource: function(scriptOrResource) 575 { 576 if (scriptOrResource instanceof WebInspector.Resource) { 577 if (!WebInspector.panels.resources) 578 return null; 579 return WebInspector.panels.resources.sourceFrameForResource(scriptOrResource); 580 } 581 if (scriptOrResource instanceof WebInspector.Script) 582 return this.sourceFrameForScript(scriptOrResource); 583 }, 584 585 _showScriptOrResource: function(scriptOrResource, line, shouldHighlightLine, fromBackForwardAction) 586 { 587 if (!scriptOrResource) 588 return; 589 590 var view; 591 if (scriptOrResource instanceof WebInspector.Resource) { 592 if (!WebInspector.panels.resources) 593 return null; 594 view = WebInspector.panels.resources.resourceViewForResource(scriptOrResource); 595 view.headersVisible = false; 596 597 if (scriptOrResource.url in this._breakpointsURLMap) { 598 var sourceFrame = this._sourceFrameForScriptOrResource(scriptOrResource); 599 if (sourceFrame && !sourceFrame.breakpoints.length) { 600 var breakpoints = this._breakpointsURLMap[scriptOrResource.url]; 601 var breakpointsLength = breakpoints.length; 602 for (var i = 0; i < breakpointsLength; ++i) 603 sourceFrame.addBreakpoint(breakpoints[i]); 604 } 605 } 606 } else if (scriptOrResource instanceof WebInspector.Script) 607 view = this.scriptViewForScript(scriptOrResource); 608 609 if (!view) 610 return; 611 612 if (!fromBackForwardAction) { 613 var oldIndex = this._currentBackForwardIndex; 614 if (oldIndex >= 0) 615 this._backForwardList.splice(oldIndex + 1, this._backForwardList.length - oldIndex); 616 617 // Check for a previous entry of the same object in _backForwardList. 618 // If one is found, remove it and update _currentBackForwardIndex to match. 619 var previousEntryIndex = this._backForwardList.indexOf(scriptOrResource); 620 if (previousEntryIndex !== -1) { 621 this._backForwardList.splice(previousEntryIndex, 1); 622 --this._currentBackForwardIndex; 623 } 624 625 this._backForwardList.push(scriptOrResource); 626 ++this._currentBackForwardIndex; 627 628 this._updateBackAndForwardButtons(); 629 } 630 631 this.visibleView = view; 632 633 if (line) { 634 if (view.revealLine) 635 view.revealLine(line); 636 if (view.highlightLine && shouldHighlightLine) 637 view.highlightLine(line); 638 } 639 640 var option; 641 if (scriptOrResource instanceof WebInspector.Script) { 642 option = scriptOrResource.filesSelectOption; 643 console.assert(option); 644 } else { 645 var url = scriptOrResource.url; 646 var script = this._scriptsForURLsInFilesSelect[url]; 647 if (script) 648 option = script.filesSelectOption; 649 } 650 651 if (option) 652 this.filesSelectElement.selectedIndex = option.index; 653 }, 654 655 _addScriptToFilesMenu: function(script) 656 { 657 if (script.resource && this._scriptsForURLsInFilesSelect[script.sourceURL]) 658 return; 659 660 this._scriptsForURLsInFilesSelect[script.sourceURL] = script; 661 662 var select = this.filesSelectElement; 663 664 var option = document.createElement("option"); 665 option.representedObject = (script.resource || script); 666 option.text = (script.sourceURL ? WebInspector.displayNameForURL(script.sourceURL) : WebInspector.UIString("(program)")); 667 668 function optionCompare(a, b) 669 { 670 var aTitle = a.text.toLowerCase(); 671 var bTitle = b.text.toLowerCase(); 672 if (aTitle < bTitle) 673 return -1; 674 else if (aTitle > bTitle) 675 return 1; 676 677 var aSourceID = a.representedObject.sourceID; 678 var bSourceID = b.representedObject.sourceID; 679 if (aSourceID < bSourceID) 680 return -1; 681 else if (aSourceID > bSourceID) 682 return 1; 683 return 0; 684 } 685 686 var insertionIndex = insertionIndexForObjectInListSortedByFunction(option, select.childNodes, optionCompare); 687 if (insertionIndex < 0) 688 select.appendChild(option); 689 else 690 select.insertBefore(option, select.childNodes.item(insertionIndex)); 691 692 script.filesSelectOption = option; 693 694 // Call _showScriptOrResource if the option we just appended ended up being selected. 695 // This will happen for the first item added to the menu. 696 if (select.options[select.selectedIndex] === option) 697 this._showScriptOrResource(option.representedObject); 698 }, 699 700 _clearCurrentExecutionLine: function() 701 { 702 if (this._executionSourceFrame) 703 this._executionSourceFrame.executionLine = 0; 704 delete this._executionSourceFrame; 705 }, 706 707 _callFrameSelected: function() 708 { 709 this._clearCurrentExecutionLine(); 710 711 var callStackPane = this.sidebarPanes.callstack; 712 var currentFrame = callStackPane.selectedCallFrame; 713 if (!currentFrame) 714 return; 715 716 this.sidebarPanes.scopechain.update(currentFrame); 717 718 var scriptOrResource = this._sourceIDMap[currentFrame.sourceID]; 719 this._showScriptOrResource(scriptOrResource, currentFrame.line); 720 721 this._executionSourceFrame = this._sourceFrameForScriptOrResource(scriptOrResource); 722 if (this._executionSourceFrame) 723 this._executionSourceFrame.executionLine = currentFrame.line; 724 }, 725 726 _changeVisibleFile: function(event) 727 { 728 var select = this.filesSelectElement; 729 this._showScriptOrResource(select.options[select.selectedIndex].representedObject); 730 }, 731 732 _startSidebarResizeDrag: function(event) 733 { 734 WebInspector.elementDragStart(this.sidebarElement, this._sidebarResizeDrag.bind(this), this._endSidebarResizeDrag.bind(this), event, "col-resize"); 735 736 if (event.target === this.sidebarResizeWidgetElement) 737 this._dragOffset = (event.target.offsetWidth - (event.pageX - event.target.totalOffsetLeft)); 738 else 739 this._dragOffset = 0; 740 }, 741 742 _endSidebarResizeDrag: function(event) 743 { 744 WebInspector.elementDragEnd(event); 745 746 delete this._dragOffset; 747 }, 748 749 _sidebarResizeDrag: function(event) 750 { 751 var x = event.pageX + this._dragOffset; 752 var newWidth = Number.constrain(window.innerWidth - x, Preferences.minScriptsSidebarWidth, window.innerWidth * 0.66); 753 754 this.sidebarElement.style.width = newWidth + "px"; 755 this.sidebarButtonsElement.style.width = newWidth + "px"; 756 this.viewsContainerElement.style.right = newWidth + "px"; 757 this.sidebarResizeWidgetElement.style.right = newWidth + "px"; 758 this.sidebarResizeElement.style.right = (newWidth - 3) + "px"; 759 760 event.preventDefault(); 761 }, 762 763 _updatePauseOnExceptionsButton: function() 764 { 765 if (InspectorController.pauseOnExceptions()) { 766 this.pauseOnExceptionButton.title = WebInspector.UIString("Don't pause on exceptions."); 767 this.pauseOnExceptionButton.addStyleClass("toggled-on"); 768 } else { 769 this.pauseOnExceptionButton.title = WebInspector.UIString("Pause on exceptions."); 770 this.pauseOnExceptionButton.removeStyleClass("toggled-on"); 771 } 772 }, 773 774 _updateDebuggerButtons: function() 775 { 776 if (InspectorController.debuggerEnabled()) { 777 this.enableToggleButton.title = WebInspector.UIString("Debugging enabled. Click to disable."); 778 this.enableToggleButton.addStyleClass("toggled-on"); 779 this.pauseOnExceptionButton.removeStyleClass("hidden"); 780 this.panelEnablerView.visible = false; 781 } else { 782 this.enableToggleButton.title = WebInspector.UIString("Debugging disabled. Click to enable."); 783 this.enableToggleButton.removeStyleClass("toggled-on"); 784 this.pauseOnExceptionButton.addStyleClass("hidden"); 785 this.panelEnablerView.visible = true; 786 } 787 788 this._updatePauseOnExceptionsButton(); 789 790 if (this._paused) { 791 this.pauseButton.addStyleClass("paused"); 792 793 this.pauseButton.disabled = false; 794 this.stepOverButton.disabled = false; 795 this.stepIntoButton.disabled = false; 796 this.stepOutButton.disabled = false; 797 798 this.debuggerStatusElement.textContent = WebInspector.UIString("Paused"); 799 } else { 800 this.pauseButton.removeStyleClass("paused"); 801 802 this.pauseButton.disabled = this._waitingToPause; 803 this.stepOverButton.disabled = true; 804 this.stepIntoButton.disabled = true; 805 this.stepOutButton.disabled = true; 806 807 if (this._waitingToPause) 808 this.debuggerStatusElement.textContent = WebInspector.UIString("Pausing"); 809 else if (this._stepping) 810 this.debuggerStatusElement.textContent = WebInspector.UIString("Stepping"); 811 else 812 this.debuggerStatusElement.textContent = ""; 813 } 814 }, 815 816 _updateBackAndForwardButtons: function() 817 { 818 this.backButton.disabled = this._currentBackForwardIndex <= 0; 819 this.forwardButton.disabled = this._currentBackForwardIndex >= (this._backForwardList.length - 1); 820 }, 821 822 _clearInterface: function() 823 { 824 this.sidebarPanes.callstack.update(null); 825 this.sidebarPanes.scopechain.update(null); 826 827 this._clearCurrentExecutionLine(); 828 this._updateDebuggerButtons(); 829 }, 830 831 _goBack: function() 832 { 833 if (this._currentBackForwardIndex <= 0) { 834 console.error("Can't go back from index " + this._currentBackForwardIndex); 835 return; 836 } 837 838 this._showScriptOrResource(this._backForwardList[--this._currentBackForwardIndex], null, false, true); 839 this._updateBackAndForwardButtons(); 840 }, 841 842 _goForward: function() 843 { 844 if (this._currentBackForwardIndex >= this._backForwardList.length - 1) { 845 console.error("Can't go forward from index " + this._currentBackForwardIndex); 846 return; 847 } 848 849 this._showScriptOrResource(this._backForwardList[++this._currentBackForwardIndex], null, false, true); 850 this._updateBackAndForwardButtons(); 851 }, 852 853 _enableDebugging: function() 854 { 855 if (InspectorController.debuggerEnabled()) 856 return; 857 this._toggleDebugging(this.panelEnablerView.alwaysEnabled); 858 }, 859 860 _toggleDebugging: function(optionalAlways) 861 { 862 this._paused = false; 863 this._waitingToPause = false; 864 this._stepping = false; 865 866 if (InspectorController.debuggerEnabled()) 867 InspectorController.disableDebugger(true); 868 else 869 InspectorController.enableDebugger(!!optionalAlways); 870 }, 871 872 _togglePauseOnExceptions: function() 873 { 874 InspectorController.setPauseOnExceptions(!InspectorController.pauseOnExceptions()); 875 this._updatePauseOnExceptionsButton(); 876 }, 877 878 _togglePause: function() 879 { 880 if (this._paused) { 881 this._paused = false; 882 this._waitingToPause = false; 883 InspectorController.resumeDebugger(); 884 } else { 885 this._stepping = false; 886 this._waitingToPause = true; 887 InspectorController.pauseInDebugger(); 888 } 889 890 this._clearInterface(); 891 }, 892 893 _stepOverClicked: function() 894 { 895 this._paused = false; 896 this._stepping = true; 897 898 this._clearInterface(); 899 900 InspectorController.stepOverStatementInDebugger(); 901 }, 902 903 _stepIntoClicked: function() 904 { 905 this._paused = false; 906 this._stepping = true; 907 908 this._clearInterface(); 909 910 InspectorController.stepIntoStatementInDebugger(); 911 }, 912 913 _stepOutClicked: function() 914 { 915 this._paused = false; 916 this._stepping = true; 917 918 this._clearInterface(); 919 920 InspectorController.stepOutOfFunctionInDebugger(); 921 } 922} 923 924WebInspector.ScriptsPanel.prototype.__proto__ = WebInspector.Panel.prototype; 925