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