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._presentationModel = new WebInspector.DebuggerPresentationModel(); 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._filesSelectChanged.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.toggleBreakpointsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Deactivate all breakpoints."), "toggle-breakpoints"); 109 this.toggleBreakpointsButton.toggled = true; 110 this.toggleBreakpointsButton.addEventListener("click", this.toggleBreakpointsClicked.bind(this), false); 111 this.sidebarButtonsElement.appendChild(this.toggleBreakpointsButton.element); 112 113 this.debuggerStatusElement = document.createElement("div"); 114 this.debuggerStatusElement.id = "scripts-debugger-status"; 115 this.sidebarButtonsElement.appendChild(this.debuggerStatusElement); 116 117 this.viewsContainerElement = document.createElement("div"); 118 this.viewsContainerElement.id = "script-resource-views"; 119 120 this.sidebarElement = document.createElement("div"); 121 this.sidebarElement.id = "scripts-sidebar"; 122 123 this.sidebarResizeElement = document.createElement("div"); 124 this.sidebarResizeElement.className = "sidebar-resizer-vertical"; 125 this.sidebarResizeElement.addEventListener("mousedown", this._startSidebarResizeDrag.bind(this), false); 126 127 this.sidebarResizeWidgetElement = document.createElement("div"); 128 this.sidebarResizeWidgetElement.id = "scripts-sidebar-resizer-widget"; 129 this.sidebarResizeWidgetElement.addEventListener("mousedown", this._startSidebarResizeDrag.bind(this), false); 130 this.topStatusBar.appendChild(this.sidebarResizeWidgetElement); 131 132 this.sidebarPanes = {}; 133 this.sidebarPanes.watchExpressions = new WebInspector.WatchExpressionsSidebarPane(); 134 this.sidebarPanes.callstack = new WebInspector.CallStackSidebarPane(this._presentationModel); 135 this.sidebarPanes.scopechain = new WebInspector.ScopeChainSidebarPane(); 136 this.sidebarPanes.jsBreakpoints = new WebInspector.JavaScriptBreakpointsSidebarPane(this._presentationModel, this._showSourceLine.bind(this)); 137 if (Preferences.nativeInstrumentationEnabled) { 138 this.sidebarPanes.domBreakpoints = WebInspector.domBreakpointsSidebarPane; 139 this.sidebarPanes.xhrBreakpoints = new WebInspector.XHRBreakpointsSidebarPane(); 140 this.sidebarPanes.eventListenerBreakpoints = new WebInspector.EventListenerBreakpointsSidebarPane(); 141 } 142 143 this.sidebarPanes.workers = new WebInspector.WorkersSidebarPane(); 144 145 for (var pane in this.sidebarPanes) 146 this.sidebarElement.appendChild(this.sidebarPanes[pane].element); 147 148 this.sidebarPanes.callstack.expanded = true; 149 150 this.sidebarPanes.scopechain.expanded = true; 151 this.sidebarPanes.jsBreakpoints.expanded = true; 152 153 var panelEnablerHeading = WebInspector.UIString("You need to enable debugging before you can use the Scripts panel."); 154 var panelEnablerDisclaimer = WebInspector.UIString("Enabling debugging will make scripts run slower."); 155 var panelEnablerButton = WebInspector.UIString("Enable Debugging"); 156 157 this.panelEnablerView = new WebInspector.PanelEnablerView("scripts", panelEnablerHeading, panelEnablerDisclaimer, panelEnablerButton); 158 this.panelEnablerView.addEventListener("enable clicked", this._enableDebugging, this); 159 160 this.element.appendChild(this.panelEnablerView.element); 161 this.element.appendChild(this.viewsContainerElement); 162 this.element.appendChild(this.sidebarElement); 163 this.element.appendChild(this.sidebarResizeElement); 164 165 this.enableToggleButton = new WebInspector.StatusBarButton("", "enable-toggle-status-bar-item"); 166 this.enableToggleButton.addEventListener("click", this._toggleDebugging.bind(this), false); 167 if (Preferences.debuggerAlwaysEnabled) 168 this.enableToggleButton.element.addStyleClass("hidden"); 169 170 this._pauseOnExceptionButton = new WebInspector.StatusBarButton("", "scripts-pause-on-exceptions-status-bar-item", 3); 171 this._pauseOnExceptionButton.addEventListener("click", this._togglePauseOnExceptions.bind(this), false); 172 173 this._registerShortcuts(); 174 175 this._debuggerEnabled = Preferences.debuggerAlwaysEnabled; 176 177 this.reset(); 178 179 WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerWasEnabled, this._debuggerWasEnabled, this); 180 WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerWasDisabled, this._debuggerWasDisabled, this); 181 182 this._presentationModel.addEventListener(WebInspector.DebuggerPresentationModel.Events.SourceFileAdded, this._sourceFileAdded, this) 183 this._presentationModel.addEventListener(WebInspector.DebuggerPresentationModel.Events.SourceFileChanged, this._sourceFileChanged, this); 184 this._presentationModel.addEventListener(WebInspector.DebuggerPresentationModel.Events.ConsoleMessageAdded, this._consoleMessageAdded, this); 185 this._presentationModel.addEventListener(WebInspector.DebuggerPresentationModel.Events.BreakpointAdded, this._breakpointAdded, this); 186 this._presentationModel.addEventListener(WebInspector.DebuggerPresentationModel.Events.BreakpointRemoved, this._breakpointRemoved, this); 187 this._presentationModel.addEventListener(WebInspector.DebuggerPresentationModel.Events.DebuggerPaused, this._debuggerPaused, this); 188 this._presentationModel.addEventListener(WebInspector.DebuggerPresentationModel.Events.DebuggerResumed, this._debuggerResumed, this); 189 this._presentationModel.addEventListener(WebInspector.DebuggerPresentationModel.Events.CallFrameSelected, this._callFrameSelected, this); 190 191 var enableDebugger = Preferences.debuggerAlwaysEnabled || WebInspector.settings.debuggerEnabled; 192 if (enableDebugger || InspectorFrontendHost.loadSessionSetting("debugger-enabled") === "true") 193 WebInspector.debuggerModel.enableDebugger(); 194 } 195 196 // Keep these in sync with WebCore::ScriptDebugServer 197 WebInspector.ScriptsPanel.PauseOnExceptionsState = { 198 DontPauseOnExceptions : "none", 199 PauseOnAllExceptions : "all", 200 PauseOnUncaughtExceptions: "uncaught" 201 }; 202 203 WebInspector.ScriptsPanel.BrowserBreakpointTypes = { 204 DOM: "DOM", 205 EventListener: "EventListener", 206 XHR: "XHR" 207 } 208 209 WebInspector.ScriptsPanel.prototype = { 210 get toolbarItemLabel() 211 { 212 return WebInspector.UIString("Scripts"); 213 }, 214 215 get statusBarItems() 216 { 217 return [this.enableToggleButton.element, this._pauseOnExceptionButton.element]; 218 }, 219 220 get defaultFocusedElement() 221 { 222 return this._filesSelectElement; 223 }, 224 225 get paused() 226 { 227 return this._paused; 228 }, 229 230 show: function() 231 { 232 WebInspector.Panel.prototype.show.call(this); 233 this.sidebarResizeElement.style.right = (this.sidebarElement.offsetWidth - 3) + "px"; 234 if (Preferences.nativeInstrumentationEnabled) 235 this.sidebarElement.insertBefore(this.sidebarPanes.domBreakpoints.element, this.sidebarPanes.xhrBreakpoints.element); 236 237 if (this.visibleView) 238 this.visibleView.show(this.viewsContainerElement); 239 }, 240 241 hide: function() 242 { 243 if (this.visibleView) 244 this.visibleView.hide(); 245 WebInspector.Panel.prototype.hide.call(this); 246 }, 247 248 get breakpointsActivated() 249 { 250 return this.toggleBreakpointsButton.toggled; 251 }, 252 253 _sourceFileAdded: function(event) 254 { 255 var sourceFile = event.data; 256 257 if (!sourceFile.url) { 258 // Anonymous sources are shown only when stepping. 259 return; 260 } 261 262 this._addOptionToFilesSelect(sourceFile.id); 263 264 var lastViewedURL = WebInspector.settings.lastViewedScriptFile; 265 if (this._filesSelectElement.length === 1) { 266 // Option we just added is the only option in files select. 267 // We have to show corresponding source frame immediately. 268 this._showSourceFrameAndAddToHistory(sourceFile.id); 269 // Restore original value of lastViewedScriptFile because 270 // source frame was shown as a result of initial load. 271 WebInspector.settings.lastViewedScriptFile = lastViewedURL; 272 } else if (sourceFile.url === lastViewedURL) 273 this._showSourceFrameAndAddToHistory(sourceFile.id); 274 }, 275 276 _addOptionToFilesSelect: function(sourceFileId) 277 { 278 var sourceFile = this._presentationModel.sourceFile(sourceFileId); 279 var select = this._filesSelectElement; 280 var option = document.createElement("option"); 281 option.text = sourceFile.url ? WebInspector.displayNameForURL(sourceFile.url) : WebInspector.UIString("(program)"); 282 if (sourceFile.isContentScript) 283 option.addStyleClass("extension-script"); 284 function optionCompare(a, b) 285 { 286 if (a.text === b.text) 287 return 0; 288 return a.text < b.text ? -1 : 1; 289 } 290 var insertionIndex = insertionIndexForObjectInListSortedByFunction(option, select.childNodes, optionCompare); 291 if (insertionIndex < 0) 292 select.appendChild(option); 293 else 294 select.insertBefore(option, select.childNodes.item(insertionIndex)); 295 296 option._sourceFileId = sourceFileId; 297 this._sourceFileIdToFilesSelectOption[sourceFileId] = option; 298 }, 299 300 setScriptSourceIsBeingEdited: function(sourceFileId, inEditMode) 301 { 302 var option = this._sourceFileIdToFilesSelectOption[sourceFileId]; 303 if (!option) 304 return; 305 if (inEditMode) 306 option.text = option.text.replace(/[^*]$/, "$&*"); 307 else 308 option.text = option.text.replace(/[*]$/, ""); 309 }, 310 311 addConsoleMessage: function(message) 312 { 313 if (message.isErrorOrWarning() && message.message) 314 this._presentationModel.addConsoleMessage(message); 315 }, 316 317 clearConsoleMessages: function() 318 { 319 this._presentationModel.clearConsoleMessages(); 320 for (var sourceFileId in this._sourceFileIdToSourceFrame) 321 this._sourceFileIdToSourceFrame[sourceFileId].clearMessages(); 322 }, 323 324 _consoleMessageAdded: function(event) 325 { 326 var message = event.data; 327 328 var sourceFrame = this._sourceFileIdToSourceFrame[message.sourceFileId]; 329 if (sourceFrame && sourceFrame.loaded) 330 sourceFrame.addMessageToSource(message.lineNumber, message.originalMessage); 331 }, 332 333 _breakpointAdded: function(event) 334 { 335 var breakpoint = event.data; 336 337 var sourceFrame = this._sourceFileIdToSourceFrame[breakpoint.sourceFileId]; 338 if (sourceFrame && sourceFrame.loaded) 339 sourceFrame.addBreakpoint(breakpoint.lineNumber, breakpoint.resolved, breakpoint.condition, breakpoint.enabled); 340 341 this.sidebarPanes.jsBreakpoints.addBreakpoint(breakpoint); 342 }, 343 344 _breakpointRemoved: function(event) 345 { 346 var breakpoint = event.data; 347 348 var sourceFrame = this._sourceFileIdToSourceFrame[breakpoint.sourceFileId]; 349 if (sourceFrame && sourceFrame.loaded) 350 sourceFrame.removeBreakpoint(breakpoint.lineNumber); 351 352 this.sidebarPanes.jsBreakpoints.removeBreakpoint(breakpoint.sourceFileId, breakpoint.lineNumber); 353 }, 354 355 evaluateInSelectedCallFrame: function(code, objectGroup, includeCommandLineAPI, callback) 356 { 357 var selectedCallFrame = this._presentationModel.selectedCallFrame; 358 selectedCallFrame.evaluate(code, objectGroup, includeCommandLineAPI, callback); 359 }, 360 361 getSelectedCallFrameVariables: function(callback) 362 { 363 var result = { this: true }; 364 365 var selectedCallFrame = this._presentationModel.selectedCallFrame; 366 if (!selectedCallFrame) 367 callback(result); 368 369 var pendingRequests = 0; 370 371 function propertiesCollected(properties) 372 { 373 for (var i = 0; properties && i < properties.length; ++i) 374 result[properties[i].name] = true; 375 if (--pendingRequests == 0) 376 callback(result); 377 } 378 379 for (var i = 0; i < selectedCallFrame.scopeChain.length; ++i) { 380 var scope = selectedCallFrame.scopeChain[i]; 381 var object = WebInspector.RemoteObject.fromPayload(scope.object); 382 pendingRequests++; 383 object.getAllProperties(propertiesCollected); 384 } 385 }, 386 387 _debuggerPaused: function(event) 388 { 389 var callFrames = event.data.callFrames; 390 var details = event.data.details; 391 392 this._paused = true; 393 this._waitingToPause = false; 394 this._stepping = false; 395 396 this._updateDebuggerButtons(); 397 398 WebInspector.currentPanel = this; 399 400 this.sidebarPanes.callstack.update(callFrames, details); 401 this.sidebarPanes.callstack.selectedCallFrame = this._presentationModel.selectedCallFrame; 402 403 if (details.eventType === WebInspector.DebuggerEventTypes.NativeBreakpoint) { 404 if (details.eventData.breakpointType === WebInspector.ScriptsPanel.BrowserBreakpointTypes.DOM) { 405 this.sidebarPanes.domBreakpoints.highlightBreakpoint(details.eventData); 406 function didCreateBreakpointHitStatusMessage(element) 407 { 408 this.sidebarPanes.callstack.setStatus(element); 409 } 410 this.sidebarPanes.domBreakpoints.createBreakpointHitStatusMessage(details.eventData, didCreateBreakpointHitStatusMessage.bind(this)); 411 } else if (details.eventData.breakpointType === WebInspector.ScriptsPanel.BrowserBreakpointTypes.EventListener) { 412 var eventName = details.eventData.eventName; 413 this.sidebarPanes.eventListenerBreakpoints.highlightBreakpoint(details.eventData.eventName); 414 var eventNameForUI = WebInspector.EventListenerBreakpointsSidebarPane.eventNameForUI(eventName); 415 this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on a \"%s\" Event Listener.", eventNameForUI)); 416 } else if (details.eventData.breakpointType === WebInspector.ScriptsPanel.BrowserBreakpointTypes.XHR) { 417 this.sidebarPanes.xhrBreakpoints.highlightBreakpoint(details.eventData.breakpointURL); 418 this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on a XMLHttpRequest.")); 419 } 420 } else { 421 function didGetSourceLocation(sourceFileId, lineNumber) 422 { 423 if (!sourceFileId || !this._presentationModel.findBreakpoint(sourceFileId, lineNumber)) 424 return; 425 this.sidebarPanes.jsBreakpoints.highlightBreakpoint(sourceFileId, lineNumber); 426 this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on a JavaScript breakpoint.")); 427 } 428 callFrames[0].sourceLine(didGetSourceLocation.bind(this)); 429 } 430 431 window.focus(); 432 InspectorFrontendHost.bringToFront(); 433 }, 434 435 _debuggerResumed: function() 436 { 437 this._paused = false; 438 this._waitingToPause = false; 439 this._stepping = false; 440 441 this._clearInterface(); 442 }, 443 444 _debuggerWasEnabled: function() 445 { 446 this._setPauseOnExceptions(WebInspector.settings.pauseOnExceptionStateString); 447 448 if (this._debuggerEnabled) 449 return; 450 451 InspectorFrontendHost.saveSessionSetting("debugger-enabled", "true"); 452 this._debuggerEnabled = true; 453 this.reset(true); 454 }, 455 456 _debuggerWasDisabled: function() 457 { 458 if (!this._debuggerEnabled) 459 return; 460 461 InspectorFrontendHost.saveSessionSetting("debugger-enabled", "false"); 462 this._debuggerEnabled = false; 463 this.reset(true); 464 }, 465 466 reset: function(preserveItems) 467 { 468 this.visibleView = null; 469 470 delete this.currentQuery; 471 this.searchCanceled(); 472 473 this._debuggerResumed(); 474 475 this._backForwardList = []; 476 this._currentBackForwardIndex = -1; 477 this._updateBackAndForwardButtons(); 478 479 this._sourceFileIdToSourceFrame = {}; 480 this._sourceFileIdToFilesSelectOption = {}; 481 this._filesSelectElement.removeChildren(); 482 this.functionsSelectElement.removeChildren(); 483 this.viewsContainerElement.removeChildren(); 484 485 this.sidebarPanes.jsBreakpoints.reset(); 486 this.sidebarPanes.watchExpressions.refreshExpressions(); 487 if (!preserveItems) 488 this.sidebarPanes.workers.reset(); 489 }, 490 491 get visibleView() 492 { 493 return this._visibleView; 494 }, 495 496 set visibleView(x) 497 { 498 if (this._visibleView === x) 499 return; 500 501 if (this._visibleView) 502 this._visibleView.hide(); 503 504 this._visibleView = x; 505 506 if (x) 507 x.show(this.viewsContainerElement); 508 }, 509 510 canShowAnchorLocation: function(anchor) 511 { 512 return this._debuggerEnabled && this._presentationModel.sourceFileForScriptURL(anchor.href); 513 }, 514 515 showAnchorLocation: function(anchor) 516 { 517 function didRequestSourceMapping(mapping) 518 { 519 var lineNumber = mapping.scriptLocationToSourceLine({lineNumber:anchor.getAttribute("line_number") - 1, columnNumber:0}); 520 this._showSourceLine(sourceFile.id, lineNumber); 521 } 522 var sourceFile = this._presentationModel.sourceFileForScriptURL(anchor.href); 523 sourceFile.requestSourceMapping(didRequestSourceMapping.bind(this)); 524 }, 525 526 _showSourceLine: function(sourceFileId, lineNumber) 527 { 528 var sourceFrame = this._showSourceFrameAndAddToHistory(sourceFileId); 529 sourceFrame.highlightLine(lineNumber); 530 }, 531 532 handleShortcut: function(event) 533 { 534 var shortcut = WebInspector.KeyboardShortcut.makeKeyFromEvent(event); 535 var handler = this._shortcuts[shortcut]; 536 if (handler) { 537 handler(event); 538 event.handled = true; 539 } else 540 this.sidebarPanes.callstack.handleShortcut(event); 541 }, 542 543 _showSourceFrameAndAddToHistory: function(sourceFileId) 544 { 545 var sourceFrame = this._showSourceFrame(sourceFileId); 546 547 var oldIndex = this._currentBackForwardIndex; 548 if (oldIndex >= 0) 549 this._backForwardList.splice(oldIndex + 1, this._backForwardList.length - oldIndex); 550 551 // Check for a previous entry of the same object in _backForwardList. 552 // If one is found, remove it. 553 var previousEntryIndex = this._backForwardList.indexOf(sourceFileId); 554 if (previousEntryIndex !== -1) 555 this._backForwardList.splice(previousEntryIndex, 1); 556 557 this._backForwardList.push(sourceFileId); 558 this._currentBackForwardIndex = this._backForwardList.length - 1; 559 560 this._updateBackAndForwardButtons(); 561 562 return sourceFrame; 563 }, 564 565 _showSourceFrame: function(sourceFileId) 566 { 567 var index = this._sourceFileIdToFilesSelectOption[sourceFileId].index; 568 this._filesSelectElement.selectedIndex = index; 569 570 var sourceFrame = this._sourceFrameForSourceFileId(sourceFileId); 571 this.visibleView = sourceFrame; 572 573 var sourceFile = this._presentationModel.sourceFile(sourceFileId); 574 if (sourceFile.url) 575 WebInspector.settings.lastViewedScriptFile = sourceFile.url; 576 577 return sourceFrame; 578 }, 579 580 _sourceFrameForSourceFileId: function(sourceFileId) 581 { 582 var sourceFrame = this._sourceFileIdToSourceFrame[sourceFileId]; 583 return sourceFrame || this._createSourceFrame(sourceFileId); 584 }, 585 586 _createSourceFrame: function(sourceFileId) 587 { 588 var sourceFile = this._presentationModel.sourceFile(sourceFileId); 589 var delegate = new WebInspector.SourceFrameDelegateForScriptsPanel(this._presentationModel, sourceFileId); 590 var sourceFrame = new WebInspector.SourceFrame(delegate, sourceFile.url); 591 sourceFrame._sourceFileId = sourceFileId; 592 sourceFrame.addEventListener(WebInspector.SourceFrame.Events.Loaded, this._sourceFrameLoaded, this); 593 this._sourceFileIdToSourceFrame[sourceFileId] = sourceFrame; 594 return sourceFrame; 595 }, 596 597 _sourceFileChanged: function(event) 598 { 599 var sourceFileId = event.data.id; 600 601 var oldSourceFrame = this._sourceFileIdToSourceFrame[sourceFileId]; 602 if (!oldSourceFrame) 603 return; 604 oldSourceFrame.removeEventListener(WebInspector.SourceFrame.Events.Loaded, this._sourceFrameLoaded, this); 605 delete this._sourceFileIdToSourceFrame[sourceFileId]; 606 if (this.visibleView !== oldSourceFrame) 607 return; 608 609 var newSourceFrame = this._createSourceFrame(sourceFileId) 610 newSourceFrame.scrollTop = oldSourceFrame.scrollTop; 611 this.visibleView = newSourceFrame; 612 }, 613 614 _sourceFrameLoaded: function(event) 615 { 616 var sourceFrame = event.target; 617 var sourceFileId = sourceFrame._sourceFileId; 618 var sourceFile = this._presentationModel.sourceFile(sourceFileId); 619 620 var messages = sourceFile.messages; 621 for (var i = 0; i < messages.length; ++i) { 622 var message = messages[i]; 623 sourceFrame.addMessageToSource(message.lineNumber, message.originalMessage); 624 } 625 626 var breakpoints = this._presentationModel.breakpointsForSourceFileId(sourceFileId); 627 for (var i = 0; i < breakpoints.length; ++i) { 628 var breakpoint = breakpoints[i]; 629 sourceFrame.addBreakpoint(breakpoint.lineNumber, breakpoint.resolved, breakpoint.condition, breakpoint.enabled); 630 } 631 }, 632 633 _clearCurrentExecutionLine: function() 634 { 635 if (this._executionSourceFrame) 636 this._executionSourceFrame.clearExecutionLine(); 637 delete this._executionSourceFrame; 638 }, 639 640 _callFrameSelected: function(event) 641 { 642 var callFrame = event.data; 643 644 this._clearCurrentExecutionLine(); 645 646 if (!callFrame) 647 return; 648 649 this.sidebarPanes.scopechain.update(callFrame); 650 this.sidebarPanes.watchExpressions.refreshExpressions(); 651 this.sidebarPanes.callstack.selectedCallFrame = this._presentationModel.selectedCallFrame; 652 653 function didGetSourceLocation(sourceFileId, lineNumber) 654 { 655 if (!sourceFileId) 656 return; 657 658 if (!(sourceFileId in this._sourceFileIdToFilesSelectOption)) { 659 // Anonymous scripts are not added to files select by default. 660 this._addOptionToFilesSelect(sourceFileId); 661 } 662 var sourceFrame = this._showSourceFrameAndAddToHistory(sourceFileId); 663 sourceFrame.setExecutionLine(lineNumber); 664 this._executionSourceFrame = sourceFrame; 665 } 666 callFrame.sourceLine(didGetSourceLocation.bind(this)); 667 }, 668 669 _filesSelectChanged: function() 670 { 671 var sourceFileId = this._filesSelectElement[this._filesSelectElement.selectedIndex]._sourceFileId; 672 this._showSourceFrameAndAddToHistory(sourceFileId); 673 }, 674 675 _startSidebarResizeDrag: function(event) 676 { 677 WebInspector.elementDragStart(this.sidebarElement, this._sidebarResizeDrag.bind(this), this._endSidebarResizeDrag.bind(this), event, "col-resize"); 678 679 if (event.target === this.sidebarResizeWidgetElement) 680 this._dragOffset = (event.target.offsetWidth - (event.pageX - event.target.totalOffsetLeft)); 681 else 682 this._dragOffset = 0; 683 }, 684 685 _endSidebarResizeDrag: function(event) 686 { 687 WebInspector.elementDragEnd(event); 688 delete this._dragOffset; 689 this.saveSidebarWidth(); 690 }, 691 692 _sidebarResizeDrag: function(event) 693 { 694 var x = event.pageX + this._dragOffset; 695 var newWidth = Number.constrain(window.innerWidth - x, Preferences.minScriptsSidebarWidth, window.innerWidth * 0.66); 696 this.setSidebarWidth(newWidth); 697 event.preventDefault(); 698 }, 699 700 setSidebarWidth: function(newWidth) 701 { 702 this.sidebarElement.style.width = newWidth + "px"; 703 this.sidebarButtonsElement.style.width = newWidth + "px"; 704 this.viewsContainerElement.style.right = newWidth + "px"; 705 this.sidebarResizeWidgetElement.style.right = newWidth + "px"; 706 this.sidebarResizeElement.style.right = (newWidth - 3) + "px"; 707 708 this.resize(); 709 }, 710 711 _setPauseOnExceptions: function(pauseOnExceptionsState) 712 { 713 pauseOnExceptionsState = pauseOnExceptionsState || WebInspector.ScriptsPanel.PauseOnExceptionsState.DontPauseOnExceptions; 714 function callback(error) 715 { 716 if (error) 717 return; 718 if (pauseOnExceptionsState == WebInspector.ScriptsPanel.PauseOnExceptionsState.DontPauseOnExceptions) 719 this._pauseOnExceptionButton.title = WebInspector.UIString("Don't pause on exceptions.\nClick to Pause on all exceptions."); 720 else if (pauseOnExceptionsState == WebInspector.ScriptsPanel.PauseOnExceptionsState.PauseOnAllExceptions) 721 this._pauseOnExceptionButton.title = WebInspector.UIString("Pause on all exceptions.\nClick to Pause on uncaught exceptions."); 722 else if (pauseOnExceptionsState == WebInspector.ScriptsPanel.PauseOnExceptionsState.PauseOnUncaughtExceptions) 723 this._pauseOnExceptionButton.title = WebInspector.UIString("Pause on uncaught exceptions.\nClick to Not pause on exceptions."); 724 725 this._pauseOnExceptionButton.state = pauseOnExceptionsState; 726 WebInspector.settings.pauseOnExceptionStateString = pauseOnExceptionsState; 727 } 728 DebuggerAgent.setPauseOnExceptions(pauseOnExceptionsState, callback.bind(this)); 729 }, 730 731 _updateDebuggerButtons: function() 732 { 733 if (this._debuggerEnabled) { 734 this.enableToggleButton.title = WebInspector.UIString("Debugging enabled. Click to disable."); 735 this.enableToggleButton.toggled = true; 736 this._pauseOnExceptionButton.visible = true; 737 this.panelEnablerView.visible = false; 738 } else { 739 this.enableToggleButton.title = WebInspector.UIString("Debugging disabled. Click to enable."); 740 this.enableToggleButton.toggled = false; 741 this._pauseOnExceptionButton.visible = false; 742 this.panelEnablerView.visible = true; 743 } 744 745 if (this._paused) { 746 this.pauseButton.addStyleClass("paused"); 747 748 this.pauseButton.disabled = false; 749 this.stepOverButton.disabled = false; 750 this.stepIntoButton.disabled = false; 751 this.stepOutButton.disabled = false; 752 753 this.debuggerStatusElement.textContent = WebInspector.UIString("Paused"); 754 } else { 755 this.pauseButton.removeStyleClass("paused"); 756 757 this.pauseButton.disabled = this._waitingToPause; 758 this.stepOverButton.disabled = true; 759 this.stepIntoButton.disabled = true; 760 this.stepOutButton.disabled = true; 761 762 if (this._waitingToPause) 763 this.debuggerStatusElement.textContent = WebInspector.UIString("Pausing"); 764 else if (this._stepping) 765 this.debuggerStatusElement.textContent = WebInspector.UIString("Stepping"); 766 else 767 this.debuggerStatusElement.textContent = ""; 768 } 769 }, 770 771 _updateBackAndForwardButtons: function() 772 { 773 this.backButton.disabled = this._currentBackForwardIndex <= 0; 774 this.forwardButton.disabled = this._currentBackForwardIndex >= (this._backForwardList.length - 1); 775 }, 776 777 _clearInterface: function() 778 { 779 this.sidebarPanes.callstack.update(null); 780 this.sidebarPanes.scopechain.update(null); 781 this.sidebarPanes.jsBreakpoints.clearBreakpointHighlight(); 782 if (Preferences.nativeInstrumentationEnabled) { 783 this.sidebarPanes.domBreakpoints.clearBreakpointHighlight(); 784 this.sidebarPanes.eventListenerBreakpoints.clearBreakpointHighlight(); 785 this.sidebarPanes.xhrBreakpoints.clearBreakpointHighlight(); 786 } 787 788 this._clearCurrentExecutionLine(); 789 this._updateDebuggerButtons(); 790 }, 791 792 _goBack: function() 793 { 794 if (this._currentBackForwardIndex <= 0) { 795 console.error("Can't go back from index " + this._currentBackForwardIndex); 796 return; 797 } 798 799 this._showSourceFrame(this._backForwardList[--this._currentBackForwardIndex]); 800 this._updateBackAndForwardButtons(); 801 }, 802 803 _goForward: function() 804 { 805 if (this._currentBackForwardIndex >= this._backForwardList.length - 1) { 806 console.error("Can't go forward from index " + this._currentBackForwardIndex); 807 return; 808 } 809 810 this._showSourceFrame(this._backForwardList[++this._currentBackForwardIndex]); 811 this._updateBackAndForwardButtons(); 812 }, 813 814 _enableDebugging: function() 815 { 816 if (this._debuggerEnabled) 817 return; 818 this._toggleDebugging(this.panelEnablerView.alwaysEnabled); 819 }, 820 821 _toggleDebugging: function(optionalAlways) 822 { 823 this._paused = false; 824 this._waitingToPause = false; 825 this._stepping = false; 826 827 if (this._debuggerEnabled) { 828 WebInspector.settings.debuggerEnabled = false; 829 WebInspector.debuggerModel.disableDebugger(); 830 } else { 831 WebInspector.settings.debuggerEnabled = !!optionalAlways; 832 WebInspector.debuggerModel.enableDebugger(); 833 } 834 }, 835 836 _togglePauseOnExceptions: function() 837 { 838 var nextStateMap = {}; 839 var stateEnum = WebInspector.ScriptsPanel.PauseOnExceptionsState; 840 nextStateMap[stateEnum.DontPauseOnExceptions] = stateEnum.PauseOnAllExceptions; 841 nextStateMap[stateEnum.PauseOnAllExceptions] = stateEnum.PauseOnUncaughtExceptions; 842 nextStateMap[stateEnum.PauseOnUncaughtExceptions] = stateEnum.DontPauseOnExceptions; 843 this._setPauseOnExceptions(nextStateMap[this._pauseOnExceptionButton.state]); 844 }, 845 846 _togglePause: function() 847 { 848 if (this._paused) { 849 this._paused = false; 850 this._waitingToPause = false; 851 DebuggerAgent.resume(); 852 } else { 853 this._stepping = false; 854 this._waitingToPause = true; 855 DebuggerAgent.pause(); 856 } 857 858 this._clearInterface(); 859 }, 860 861 _stepOverClicked: function() 862 { 863 this._paused = false; 864 this._stepping = true; 865 866 this._clearInterface(); 867 868 DebuggerAgent.stepOver(); 869 }, 870 871 _stepIntoClicked: function() 872 { 873 this._paused = false; 874 this._stepping = true; 875 876 this._clearInterface(); 877 878 DebuggerAgent.stepInto(); 879 }, 880 881 _stepOutClicked: function() 882 { 883 this._paused = false; 884 this._stepping = true; 885 886 this._clearInterface(); 887 888 DebuggerAgent.stepOut(); 889 }, 890 891 toggleBreakpointsClicked: function() 892 { 893 this.toggleBreakpointsButton.toggled = !this.toggleBreakpointsButton.toggled; 894 if (this.toggleBreakpointsButton.toggled) { 895 DebuggerAgent.setBreakpointsActive(true); 896 this.toggleBreakpointsButton.title = WebInspector.UIString("Deactivate all breakpoints."); 897 document.getElementById("main-panels").removeStyleClass("breakpoints-deactivated"); 898 } else { 899 DebuggerAgent.setBreakpointsActive(false); 900 this.toggleBreakpointsButton.title = WebInspector.UIString("Activate all breakpoints."); 901 document.getElementById("main-panels").addStyleClass("breakpoints-deactivated"); 902 } 903 }, 904 905 elementsToRestoreScrollPositionsFor: function() 906 { 907 return [ this.sidebarElement ]; 908 }, 909 910 _registerShortcuts: function() 911 { 912 var section = WebInspector.shortcutsHelp.section(WebInspector.UIString("Scripts Panel")); 913 var handler, shortcut1, shortcut2; 914 var platformSpecificModifier = WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta; 915 916 this._shortcuts = {}; 917 918 // Continue. 919 handler = this.pauseButton.click.bind(this.pauseButton); 920 shortcut1 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.F8); 921 this._shortcuts[shortcut1.key] = handler; 922 shortcut2 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Slash, platformSpecificModifier); 923 this._shortcuts[shortcut2.key] = handler; 924 section.addAlternateKeys([ shortcut1.name, shortcut2.name ], WebInspector.UIString("Continue")); 925 926 // Step over. 927 handler = this.stepOverButton.click.bind(this.stepOverButton); 928 shortcut1 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.F10); 929 this._shortcuts[shortcut1.key] = handler; 930 shortcut2 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.SingleQuote, platformSpecificModifier); 931 this._shortcuts[shortcut2.key] = handler; 932 section.addAlternateKeys([ shortcut1.name, shortcut2.name ], WebInspector.UIString("Step over")); 933 934 // Step into. 935 handler = this.stepIntoButton.click.bind(this.stepIntoButton); 936 shortcut1 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.F11); 937 this._shortcuts[shortcut1.key] = handler; 938 shortcut2 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Semicolon, platformSpecificModifier); 939 this._shortcuts[shortcut2.key] = handler; 940 section.addAlternateKeys([ shortcut1.name, shortcut2.name ], WebInspector.UIString("Step into")); 941 942 // Step out. 943 handler = this.stepOutButton.click.bind(this.stepOutButton); 944 shortcut1 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.F11, WebInspector.KeyboardShortcut.Modifiers.Shift); 945 this._shortcuts[shortcut1.key] = handler; 946 shortcut2 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Semicolon, WebInspector.KeyboardShortcut.Modifiers.Shift, platformSpecificModifier); 947 this._shortcuts[shortcut2.key] = handler; 948 section.addAlternateKeys([ shortcut1.name, shortcut2.name ], WebInspector.UIString("Step out")); 949 950 var isMac = WebInspector.isMac(); 951 if (isMac) 952 shortcut1 = WebInspector.KeyboardShortcut.makeDescriptor("l", WebInspector.KeyboardShortcut.Modifiers.Meta); 953 else 954 shortcut1 = WebInspector.KeyboardShortcut.makeDescriptor("g", WebInspector.KeyboardShortcut.Modifiers.Ctrl); 955 this._shortcuts[shortcut1.key] = this.showGoToLineDialog.bind(this); 956 section.addAlternateKeys([ shortcut1.name ], WebInspector.UIString("Go to Line")); 957 this.sidebarPanes.callstack.registerShortcuts(section); 958 }, 959 960 searchCanceled: function() 961 { 962 if (this._searchView) 963 this._searchView.searchCanceled(); 964 965 delete this._searchView; 966 delete this._searchQuery; 967 }, 968 969 performSearch: function(query) 970 { 971 WebInspector.searchController.updateSearchMatchesCount(0, this); 972 973 if (!this.visibleView) 974 return; 975 976 // Call searchCanceled since it will reset everything we need before doing a new search. 977 this.searchCanceled(); 978 979 this._searchView = this.visibleView; 980 this._searchQuery = query; 981 982 function finishedCallback(view, searchMatches) 983 { 984 if (!searchMatches) 985 return; 986 987 WebInspector.searchController.updateSearchMatchesCount(searchMatches, this); 988 view.jumpToFirstSearchResult(); 989 } 990 991 this._searchView.performSearch(query, finishedCallback.bind(this)); 992 }, 993 994 jumpToNextSearchResult: function() 995 { 996 if (!this._searchView) 997 return; 998 999 if (this._searchView !== this.visibleView) { 1000 this.performSearch(this._searchQuery); 1001 return; 1002 } 1003 1004 if (this._searchView.showingLastSearchResult()) 1005 this._searchView.jumpToFirstSearchResult(); 1006 else 1007 this._searchView.jumpToNextSearchResult(); 1008 }, 1009 1010 jumpToPreviousSearchResult: function() 1011 { 1012 if (!this._searchView) 1013 return; 1014 1015 if (this._searchView !== this.visibleView) { 1016 this.performSearch(this._searchQuery); 1017 if (this._searchView) 1018 this._searchView.jumpToLastSearchResult(); 1019 return; 1020 } 1021 1022 if (this._searchView.showingFirstSearchResult()) 1023 this._searchView.jumpToLastSearchResult(); 1024 else 1025 this._searchView.jumpToPreviousSearchResult(); 1026 }, 1027 1028 showGoToLineDialog: function(e) 1029 { 1030 var view = this.visibleView; 1031 if (view) 1032 WebInspector.GoToLineDialog.show(view); 1033 } 1034 } 1035 1036 WebInspector.ScriptsPanel.prototype.__proto__ = WebInspector.Panel.prototype; 1037 1038 1039 WebInspector.SourceFrameDelegateForScriptsPanel = function(model, sourceFileId) 1040 { 1041 WebInspector.SourceFrameDelegate.call(this); 1042 this._model = model; 1043 this._sourceFileId = sourceFileId; 1044 this._popoverObjectGroup = "popover"; 1045 } 1046 1047 WebInspector.SourceFrameDelegateForScriptsPanel.prototype = { 1048 requestContent: function(callback) 1049 { 1050 this._model.requestSourceFileContent(this._sourceFileId, callback); 1051 }, 1052 1053 debuggingSupported: function() 1054 { 1055 return true; 1056 }, 1057 1058 setBreakpoint: function(lineNumber, condition, enabled) 1059 { 1060 this._model.setBreakpoint(this._sourceFileId, lineNumber, condition, enabled); 1061 1062 if (!WebInspector.panels.scripts.breakpointsActivated) 1063 WebInspector.panels.scripts.toggleBreakpointsClicked(); 1064 }, 1065 1066 updateBreakpoint: function(lineNumber, condition, enabled) 1067 { 1068 this._model.updateBreakpoint(this._sourceFileId, lineNumber, condition, enabled); 1069 }, 1070 1071 removeBreakpoint: function(lineNumber) 1072 { 1073 this._model.removeBreakpoint(this._sourceFileId, lineNumber); 1074 }, 1075 1076 findBreakpoint: function(lineNumber) 1077 { 1078 return this._model.findBreakpoint(this._sourceFileId, lineNumber); 1079 }, 1080 1081 continueToLine: function(lineNumber) 1082 { 1083 this._model.continueToLine(this._sourceFileId, lineNumber); 1084 }, 1085 1086 canEditScriptSource: function() 1087 { 1088 return this._model.canEditScriptSource(this._sourceFileId); 1089 }, 1090 1091 editScriptSource: function(text, callback) 1092 { 1093 this._model.editScriptSource(this._sourceFileId, text, callback); 1094 }, 1095 1096 setScriptSourceIsBeingEdited: function(inEditMode) 1097 { 1098 WebInspector.panels.scripts.setScriptSourceIsBeingEdited(this._sourceFileId, inEditMode); 1099 }, 1100 1101 debuggerPaused: function() 1102 { 1103 return WebInspector.panels.scripts.paused; 1104 }, 1105 1106 evaluateInSelectedCallFrame: function(string, callback) 1107 { 1108 WebInspector.panels.scripts.evaluateInSelectedCallFrame(string, this._popoverObjectGroup, false, callback); 1109 }, 1110 1111 releaseEvaluationResult: function() 1112 { 1113 RuntimeAgent.releaseObjectGroup(this._popoverObjectGroup); 1114 }, 1115 1116 toggleFormatSourceFiles: function() 1117 { 1118 WebInspector.panels.scripts.reset(); 1119 this._model.toggleFormatSourceFiles(); 1120 }, 1121 1122 formatSourceFilesToggled: function() 1123 { 1124 return this._model.formatSourceFilesToggled(); 1125 } 1126 } 1127 1128 WebInspector.SourceFrameDelegateForScriptsPanel.prototype.__proto__ = WebInspector.SourceFrameDelegate.prototype; 1129