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 /** 27 * @constructor 28 * @extends {WebInspector.SidebarPane} 29 * @param {!WebInspector.BreakpointManager} breakpointManager 30 * @param {function(!WebInspector.UISourceCode, number=, number=, boolean=)} showSourceLineDelegate 31 */ 32 WebInspector.JavaScriptBreakpointsSidebarPane = function(breakpointManager, showSourceLineDelegate) 33 { 34 WebInspector.SidebarPane.call(this, WebInspector.UIString("Breakpoints")); 35 this.registerRequiredCSS("breakpointsList.css"); 36 37 this._breakpointManager = breakpointManager; 38 this._showSourceLineDelegate = showSourceLineDelegate; 39 40 this.listElement = document.createElementWithClass("ol", "breakpoint-list"); 41 42 this.emptyElement = this.bodyElement.createChild("div", "info"); 43 this.emptyElement.textContent = WebInspector.UIString("No Breakpoints"); 44 45 this._items = new Map(); 46 47 var breakpointLocations = this._breakpointManager.allBreakpointLocations(); 48 for (var i = 0; i < breakpointLocations.length; ++i) 49 this._addBreakpoint(breakpointLocations[i].breakpoint, breakpointLocations[i].uiLocation); 50 51 this._breakpointManager.addEventListener(WebInspector.BreakpointManager.Events.BreakpointAdded, this._breakpointAdded, this); 52 this._breakpointManager.addEventListener(WebInspector.BreakpointManager.Events.BreakpointRemoved, this._breakpointRemoved, this); 53 54 this.emptyElement.addEventListener("contextmenu", this._emptyElementContextMenu.bind(this), true); 55 } 56 57 WebInspector.JavaScriptBreakpointsSidebarPane.prototype = { 58 _emptyElementContextMenu: function(event) 59 { 60 var contextMenu = new WebInspector.ContextMenu(event); 61 this._appendBreakpointActiveItem(contextMenu); 62 contextMenu.show(); 63 }, 64 65 /** 66 * @param {!WebInspector.ContextMenu} contextMenu 67 */ 68 _appendBreakpointActiveItem: function(contextMenu) 69 { 70 var breakpointActive = this._breakpointManager.breakpointsActive(); 71 var breakpointActiveTitle = breakpointActive ? 72 WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Deactivate breakpoints" : "Deactivate Breakpoints") : 73 WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Activate breakpoints" : "Activate Breakpoints"); 74 contextMenu.appendItem(breakpointActiveTitle, this._breakpointManager.setBreakpointsActive.bind(this._breakpointManager, !breakpointActive)); 75 }, 76 77 /** 78 * @param {!WebInspector.Event} event 79 */ 80 _breakpointAdded: function(event) 81 { 82 this._breakpointRemoved(event); 83 84 var breakpoint = /** @type {!WebInspector.BreakpointManager.Breakpoint} */ (event.data.breakpoint); 85 var uiLocation = /** @type {!WebInspector.UILocation} */ (event.data.uiLocation); 86 this._addBreakpoint(breakpoint, uiLocation); 87 }, 88 89 /** 90 * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint 91 * @param {!WebInspector.UILocation} uiLocation 92 */ 93 _addBreakpoint: function(breakpoint, uiLocation) 94 { 95 var element = document.createElementWithClass("li", "cursor-pointer"); 96 element.addEventListener("contextmenu", this._breakpointContextMenu.bind(this, breakpoint), true); 97 element.addEventListener("click", this._breakpointClicked.bind(this, uiLocation), false); 98 99 var checkbox = element.createChild("input", "checkbox-elem"); 100 checkbox.type = "checkbox"; 101 checkbox.checked = breakpoint.enabled(); 102 checkbox.addEventListener("click", this._breakpointCheckboxClicked.bind(this, breakpoint), false); 103 104 element.createTextChild(uiLocation.linkText()); 105 106 var snippetElement = element.createChild("div", "source-text monospace"); 107 108 /** 109 * @param {?string} content 110 */ 111 function didRequestContent(content) 112 { 113 var lineNumber = uiLocation.lineNumber 114 var columnNumber = uiLocation.columnNumber; 115 var contentString = new String(content); 116 if (lineNumber < contentString.lineCount()) { 117 var lineText = contentString.lineAt(lineNumber); 118 var maxSnippetLength = 200; 119 var snippetStartIndex = columnNumber > 100 ? columnNumber : 0; 120 snippetElement.textContent = lineText.substr(snippetStartIndex).trimEnd(maxSnippetLength); 121 } 122 } 123 124 uiLocation.uiSourceCode.requestContent(didRequestContent); 125 126 element._data = uiLocation; 127 var currentElement = this.listElement.firstChild; 128 while (currentElement) { 129 if (currentElement._data && this._compareBreakpoints(currentElement._data, element._data) > 0) 130 break; 131 currentElement = currentElement.nextSibling; 132 } 133 this._addListElement(element, currentElement); 134 135 var breakpointItem = { element: element, checkbox: checkbox }; 136 this._items.set(breakpoint, breakpointItem); 137 138 this.expand(); 139 }, 140 141 /** 142 * @param {!WebInspector.Event} event 143 */ 144 _breakpointRemoved: function(event) 145 { 146 var breakpoint = /** @type {!WebInspector.BreakpointManager.Breakpoint} */ (event.data.breakpoint); 147 var uiLocation = /** @type {!WebInspector.UILocation} */ (event.data.uiLocation); 148 var breakpointItem = this._items.get(breakpoint); 149 if (!breakpointItem) 150 return; 151 this._items.remove(breakpoint); 152 this._removeListElement(breakpointItem.element); 153 }, 154 155 /** 156 * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint 157 */ 158 highlightBreakpoint: function(breakpoint) 159 { 160 var breakpointItem = this._items.get(breakpoint); 161 if (!breakpointItem) 162 return; 163 breakpointItem.element.classList.add("breakpoint-hit"); 164 this._highlightedBreakpointItem = breakpointItem; 165 }, 166 167 clearBreakpointHighlight: function() 168 { 169 if (this._highlightedBreakpointItem) { 170 this._highlightedBreakpointItem.element.classList.remove("breakpoint-hit"); 171 delete this._highlightedBreakpointItem; 172 } 173 }, 174 175 _breakpointClicked: function(uiLocation, event) 176 { 177 this._showSourceLineDelegate(uiLocation.uiSourceCode, uiLocation.lineNumber); 178 }, 179 180 /** 181 * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint 182 * @param {!Event} event 183 */ 184 _breakpointCheckboxClicked: function(breakpoint, event) 185 { 186 // Breakpoint element has it's own click handler. 187 event.consume(); 188 breakpoint.setEnabled(event.target.checked); 189 }, 190 191 /** 192 * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint 193 * @param {!Event} event 194 */ 195 _breakpointContextMenu: function(breakpoint, event) 196 { 197 var breakpoints = this._items.values(); 198 var contextMenu = new WebInspector.ContextMenu(event); 199 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Remove breakpoint" : "Remove Breakpoint"), breakpoint.remove.bind(breakpoint)); 200 if (breakpoints.length > 1) { 201 var removeAllTitle = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Remove all breakpoints" : "Remove All Breakpoints"); 202 contextMenu.appendItem(removeAllTitle, this._breakpointManager.removeAllBreakpoints.bind(this._breakpointManager)); 203 } 204 205 contextMenu.appendSeparator(); 206 this._appendBreakpointActiveItem(contextMenu); 207 208 function enabledBreakpointCount(breakpoints) 209 { 210 var count = 0; 211 for (var i = 0; i < breakpoints.length; ++i) { 212 if (breakpoints[i].checkbox.checked) 213 count++; 214 } 215 return count; 216 } 217 if (breakpoints.length > 1) { 218 var enableBreakpointCount = enabledBreakpointCount(breakpoints); 219 var enableTitle = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Enable all breakpoints" : "Enable All Breakpoints"); 220 var disableTitle = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Disable all breakpoints" : "Disable All Breakpoints"); 221 222 contextMenu.appendSeparator(); 223 224 contextMenu.appendItem(enableTitle, this._breakpointManager.toggleAllBreakpoints.bind(this._breakpointManager, true), !(enableBreakpointCount != breakpoints.length)); 225 contextMenu.appendItem(disableTitle, this._breakpointManager.toggleAllBreakpoints.bind(this._breakpointManager, false), !(enableBreakpointCount > 1)); 226 } 227 228 contextMenu.show(); 229 }, 230 231 _addListElement: function(element, beforeElement) 232 { 233 if (beforeElement) 234 this.listElement.insertBefore(element, beforeElement); 235 else { 236 if (!this.listElement.firstChild) { 237 this.bodyElement.removeChild(this.emptyElement); 238 this.bodyElement.appendChild(this.listElement); 239 } 240 this.listElement.appendChild(element); 241 } 242 }, 243 244 _removeListElement: function(element) 245 { 246 this.listElement.removeChild(element); 247 if (!this.listElement.firstChild) { 248 this.bodyElement.removeChild(this.listElement); 249 this.bodyElement.appendChild(this.emptyElement); 250 } 251 }, 252 253 _compare: function(x, y) 254 { 255 if (x !== y) 256 return x < y ? -1 : 1; 257 return 0; 258 }, 259 260 _compareBreakpoints: function(b1, b2) 261 { 262 return this._compare(b1.uiSourceCode.originURL(), b2.uiSourceCode.originURL()) || this._compare(b1.lineNumber, b2.lineNumber); 263 }, 264 265 reset: function() 266 { 267 this.listElement.removeChildren(); 268 if (this.listElement.parentElement) { 269 this.bodyElement.removeChild(this.listElement); 270 this.bodyElement.appendChild(this.emptyElement); 271 } 272 this._items.clear(); 273 }, 274 275 __proto__: WebInspector.SidebarPane.prototype 276 } 277 278 /** 279 * @constructor 280 * @extends {WebInspector.NativeBreakpointsSidebarPane} 281 * @implements {WebInspector.TargetManager.Observer} 282 */ 283 WebInspector.XHRBreakpointsSidebarPane = function() 284 { 285 WebInspector.NativeBreakpointsSidebarPane.call(this, WebInspector.UIString("XHR Breakpoints")); 286 287 // FIXME: Use StringMap. 288 this._breakpointElements = { __proto__: null }; 289 290 var addButton = this.titleElement.createChild("button", "pane-title-button add"); 291 addButton.title = WebInspector.UIString("Add XHR breakpoint"); 292 addButton.addEventListener("click", this._addButtonClicked.bind(this), false); 293 294 this.emptyElement.addEventListener("contextmenu", this._emptyElementContextMenu.bind(this), true); 295 296 WebInspector.targetManager.observeTargets(this); 297 } 298 299 WebInspector.XHRBreakpointsSidebarPane.prototype = { 300 /** 301 * @param {!WebInspector.Target} target 302 */ 303 targetAdded: function(target) 304 { 305 this._restoreBreakpoints(target); 306 }, 307 308 /** 309 * @param {!WebInspector.Target} target 310 */ 311 targetRemoved: function(target) { }, 312 313 _emptyElementContextMenu: function(event) 314 { 315 var contextMenu = new WebInspector.ContextMenu(event); 316 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add breakpoint" : "Add Breakpoint"), this._addButtonClicked.bind(this)); 317 contextMenu.show(); 318 }, 319 320 _addButtonClicked: function(event) 321 { 322 if (event) 323 event.consume(); 324 325 this.expand(); 326 327 var inputElementContainer = document.createElementWithClass("p", "breakpoint-condition"); 328 inputElementContainer.textContent = WebInspector.UIString("Break when URL contains:"); 329 330 var inputElement = inputElementContainer.createChild("span", "editing"); 331 inputElement.id = "breakpoint-condition-input"; 332 this.addListElement(inputElementContainer, /** @type {?Element} */ (this.listElement.firstChild)); 333 334 /** 335 * @param {boolean} accept 336 * @param {!Element} e 337 * @param {string} text 338 * @this {WebInspector.XHRBreakpointsSidebarPane} 339 */ 340 function finishEditing(accept, e, text) 341 { 342 this.removeListElement(inputElementContainer); 343 if (accept) { 344 this._setBreakpoint(text, true); 345 this._saveBreakpoints(); 346 } 347 } 348 349 var config = new WebInspector.InplaceEditor.Config(finishEditing.bind(this, true), finishEditing.bind(this, false)); 350 WebInspector.InplaceEditor.startEditing(inputElement, config); 351 }, 352 353 /** 354 * @param {string} url 355 * @param {boolean} enabled 356 * @param {!WebInspector.Target=} target 357 */ 358 _setBreakpoint: function(url, enabled, target) 359 { 360 if (enabled) 361 this._updateBreakpointOnTarget(url, true, target); 362 363 if (url in this._breakpointElements) 364 return; 365 366 var element = document.createElement("li"); 367 element._url = url; 368 element.addEventListener("contextmenu", this._contextMenu.bind(this, url), true); 369 370 var checkboxElement = element.createChild("input", "checkbox-elem"); 371 checkboxElement.type = "checkbox"; 372 checkboxElement.checked = enabled; 373 checkboxElement.addEventListener("click", this._checkboxClicked.bind(this, url), false); 374 element._checkboxElement = checkboxElement; 375 376 var labelElement = element.createChild("span", "cursor-auto"); 377 if (!url) 378 labelElement.textContent = WebInspector.UIString("Any XHR"); 379 else 380 labelElement.textContent = WebInspector.UIString("URL contains \"%s\"", url); 381 labelElement.addEventListener("dblclick", this._labelClicked.bind(this, url), false); 382 383 var currentElement = /** @type {?Element} */ (this.listElement.firstChild); 384 while (currentElement) { 385 if (currentElement._url && currentElement._url < element._url) 386 break; 387 currentElement = /** @type {?Element} */ (currentElement.nextSibling); 388 } 389 this.addListElement(element, currentElement); 390 this._breakpointElements[url] = element; 391 }, 392 393 /** 394 * @param {string} url 395 * @param {!WebInspector.Target=} target 396 */ 397 _removeBreakpoint: function(url, target) 398 { 399 var element = this._breakpointElements[url]; 400 if (!element) 401 return; 402 403 this.removeListElement(element); 404 delete this._breakpointElements[url]; 405 if (element._checkboxElement.checked) 406 this._updateBreakpointOnTarget(url, false, target); 407 }, 408 409 /** 410 * @param {string} url 411 * @param {boolean} enable 412 * @param {!WebInspector.Target=} target 413 */ 414 _updateBreakpointOnTarget: function(url, enable, target) 415 { 416 var targets = target ? [target] : WebInspector.targetManager.targets(); 417 for (var i = 0; i < targets.length; ++i) { 418 if (enable) 419 targets[i].domdebuggerAgent().setXHRBreakpoint(url); 420 else 421 targets[i].domdebuggerAgent().removeXHRBreakpoint(url); 422 } 423 }, 424 425 _contextMenu: function(url, event) 426 { 427 var contextMenu = new WebInspector.ContextMenu(event); 428 429 /** 430 * @this {WebInspector.XHRBreakpointsSidebarPane} 431 */ 432 function removeBreakpoint() 433 { 434 this._removeBreakpoint(url); 435 this._saveBreakpoints(); 436 } 437 438 /** 439 * @this {WebInspector.XHRBreakpointsSidebarPane} 440 */ 441 function removeAllBreakpoints() 442 { 443 for (var url in this._breakpointElements) 444 this._removeBreakpoint(url); 445 this._saveBreakpoints(); 446 } 447 var removeAllTitle = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Remove all breakpoints" : "Remove All Breakpoints"); 448 449 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add breakpoint" : "Add Breakpoint"), this._addButtonClicked.bind(this)); 450 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Remove breakpoint" : "Remove Breakpoint"), removeBreakpoint.bind(this)); 451 contextMenu.appendItem(removeAllTitle, removeAllBreakpoints.bind(this)); 452 contextMenu.show(); 453 }, 454 455 _checkboxClicked: function(url, event) 456 { 457 this._updateBreakpointOnTarget(url, event.target.checked); 458 this._saveBreakpoints(); 459 }, 460 461 _labelClicked: function(url) 462 { 463 var element = this._breakpointElements[url]; 464 var inputElement = document.createElementWithClass("span", "breakpoint-condition editing"); 465 inputElement.textContent = url; 466 this.listElement.insertBefore(inputElement, element); 467 element.classList.add("hidden"); 468 469 /** 470 * @param {boolean} accept 471 * @param {!Element} e 472 * @param {string} text 473 * @this {WebInspector.XHRBreakpointsSidebarPane} 474 */ 475 function finishEditing(accept, e, text) 476 { 477 this.removeListElement(inputElement); 478 if (accept) { 479 this._removeBreakpoint(url); 480 this._setBreakpoint(text, element._checkboxElement.checked); 481 this._saveBreakpoints(); 482 } else 483 element.classList.remove("hidden"); 484 } 485 486 WebInspector.InplaceEditor.startEditing(inputElement, new WebInspector.InplaceEditor.Config(finishEditing.bind(this, true), finishEditing.bind(this, false))); 487 }, 488 489 highlightBreakpoint: function(url) 490 { 491 var element = this._breakpointElements[url]; 492 if (!element) 493 return; 494 this.expand(); 495 element.classList.add("breakpoint-hit"); 496 this._highlightedElement = element; 497 }, 498 499 clearBreakpointHighlight: function() 500 { 501 if (this._highlightedElement) { 502 this._highlightedElement.classList.remove("breakpoint-hit"); 503 delete this._highlightedElement; 504 } 505 }, 506 507 _saveBreakpoints: function() 508 { 509 var breakpoints = []; 510 for (var url in this._breakpointElements) 511 breakpoints.push({ url: url, enabled: this._breakpointElements[url]._checkboxElement.checked }); 512 WebInspector.settings.xhrBreakpoints.set(breakpoints); 513 }, 514 515 /** 516 * @param {!WebInspector.Target} target 517 */ 518 _restoreBreakpoints: function(target) 519 { 520 var breakpoints = WebInspector.settings.xhrBreakpoints.get(); 521 for (var i = 0; i < breakpoints.length; ++i) { 522 var breakpoint = breakpoints[i]; 523 if (breakpoint && typeof breakpoint.url === "string") 524 this._setBreakpoint(breakpoint.url, breakpoint.enabled, target); 525 } 526 }, 527 528 __proto__: WebInspector.NativeBreakpointsSidebarPane.prototype 529 } 530 531 /** 532 * @constructor 533 * @extends {WebInspector.SidebarPane} 534 * @implements {WebInspector.TargetManager.Observer} 535 */ 536 WebInspector.EventListenerBreakpointsSidebarPane = function() 537 { 538 WebInspector.SidebarPane.call(this, WebInspector.UIString("Event Listener Breakpoints")); 539 this.registerRequiredCSS("breakpointsList.css"); 540 541 this.categoriesElement = this.bodyElement.createChild("ol", "properties-tree event-listener-breakpoints"); 542 this.categoriesElement.tabIndex = 0; 543 this.categoriesTreeOutline = new TreeOutline(this.categoriesElement); 544 545 this._categoryItems = []; 546 // FIXME: uncomment following once inspector stops being drop targer in major ports. 547 // Otherwise, inspector page reacts on drop event and tries to load the event data. 548 // this._createCategory(WebInspector.UIString("Drag"), ["drag", "drop", "dragstart", "dragend", "dragenter", "dragleave", "dragover"]); 549 this._createCategory(WebInspector.UIString("Animation"), ["requestAnimationFrame", "cancelAnimationFrame", "animationFrameFired"], true); 550 this._createCategory(WebInspector.UIString("Control"), ["resize", "scroll", "zoom", "focus", "blur", "select", "change", "submit", "reset"]); 551 this._createCategory(WebInspector.UIString("Clipboard"), ["copy", "cut", "paste", "beforecopy", "beforecut", "beforepaste"]); 552 this._createCategory(WebInspector.UIString("DOM Mutation"), ["DOMActivate", "DOMFocusIn", "DOMFocusOut", "DOMAttrModified", "DOMCharacterDataModified", "DOMNodeInserted", "DOMNodeInsertedIntoDocument", "DOMNodeRemoved", "DOMNodeRemovedFromDocument", "DOMSubtreeModified", "DOMContentLoaded"]); 553 this._createCategory(WebInspector.UIString("Device"), ["deviceorientation", "devicemotion"]); 554 this._createCategory(WebInspector.UIString("Drag / drop"), ["dragenter", "dragover", "dragleave", "drop"]); 555 this._createCategory(WebInspector.UIString("Keyboard"), ["keydown", "keyup", "keypress", "input"]); 556 this._createCategory(WebInspector.UIString("Load"), ["load", "beforeunload", "unload", "abort", "error", "hashchange", "popstate"]); 557 this._createCategory(WebInspector.UIString("Media"), ["play", "pause", "playing", "canplay", "canplaythrough", "seeking", "seeked", "timeupdate", "ended", "ratechange", "durationchange", "volumechange", "loadstart", "progress", "suspend", "abort", "error", "emptied", "stalled", "loadedmetadata", "loadeddata", "waiting"], false, ["audio", "video"]); 558 this._createCategory(WebInspector.UIString("Mouse"), ["click", "dblclick", "mousedown", "mouseup", "mouseover", "mousemove", "mouseout", "mousewheel", "wheel"]); 559 this._createCategory(WebInspector.UIString("Promise"), ["newPromise", "promiseResolved", "promiseRejected"], true); 560 this._createCategory(WebInspector.UIString("Timer"), ["setTimer", "clearTimer", "timerFired"], true); 561 this._createCategory(WebInspector.UIString("Touch"), ["touchstart", "touchmove", "touchend", "touchcancel"]); 562 this._createCategory(WebInspector.UIString("XHR"), ["readystatechange", "load", "loadstart", "loadend", "abort", "error", "progress", "timeout"], false, ["XMLHttpRequest", "XMLHttpRequestUpload"]); 563 this._createCategory(WebInspector.UIString("WebGL"), ["webglErrorFired", "webglWarningFired"], true); 564 this._createCategory(WebInspector.UIString("Window"), ["close"], true); 565 566 WebInspector.targetManager.observeTargets(this); 567 } 568 569 WebInspector.EventListenerBreakpointsSidebarPane.categoryListener = "listener:"; 570 WebInspector.EventListenerBreakpointsSidebarPane.categoryInstrumentation = "instrumentation:"; 571 WebInspector.EventListenerBreakpointsSidebarPane.eventTargetAny = "*"; 572 573 /** 574 * @param {string} eventName 575 * @param {!Object=} auxData 576 * @return {string} 577 */ 578 WebInspector.EventListenerBreakpointsSidebarPane.eventNameForUI = function(eventName, auxData) 579 { 580 if (!WebInspector.EventListenerBreakpointsSidebarPane._eventNamesForUI) { 581 WebInspector.EventListenerBreakpointsSidebarPane._eventNamesForUI = { 582 "instrumentation:setTimer": WebInspector.UIString("Set Timer"), 583 "instrumentation:clearTimer": WebInspector.UIString("Clear Timer"), 584 "instrumentation:timerFired": WebInspector.UIString("Timer Fired"), 585 "instrumentation:newPromise": WebInspector.UIString("Promise Created"), 586 "instrumentation:promiseResolved": WebInspector.UIString("Promise Resolved"), 587 "instrumentation:promiseRejected": WebInspector.UIString("Promise Rejected"), 588 "instrumentation:requestAnimationFrame": WebInspector.UIString("Request Animation Frame"), 589 "instrumentation:cancelAnimationFrame": WebInspector.UIString("Cancel Animation Frame"), 590 "instrumentation:animationFrameFired": WebInspector.UIString("Animation Frame Fired"), 591 "instrumentation:webglErrorFired": WebInspector.UIString("WebGL Error Fired"), 592 "instrumentation:webglWarningFired": WebInspector.UIString("WebGL Warning Fired") 593 }; 594 } 595 if (auxData) { 596 if (eventName === "instrumentation:webglErrorFired" && auxData["webglErrorName"]) { 597 var errorName = auxData["webglErrorName"]; 598 // If there is a hex code of the error, display only this. 599 errorName = errorName.replace(/^.*(0x[0-9a-f]+).*$/i, "$1"); 600 return WebInspector.UIString("WebGL Error Fired (%s)", errorName); 601 } 602 } 603 return WebInspector.EventListenerBreakpointsSidebarPane._eventNamesForUI[eventName] || eventName.substring(eventName.indexOf(":") + 1); 604 } 605 606 WebInspector.EventListenerBreakpointsSidebarPane.prototype = { 607 /** 608 * @param {!WebInspector.Target} target 609 */ 610 targetAdded: function(target) 611 { 612 this._restoreBreakpoints(target); 613 }, 614 615 /** 616 * @param {!WebInspector.Target} target 617 */ 618 targetRemoved: function(target) { }, 619 620 /** 621 * @param {string} name 622 * @param {!Array.<string>} eventNames 623 * @param {boolean=} isInstrumentationEvent 624 * @param {!Array.<string>=} targetNames 625 */ 626 _createCategory: function(name, eventNames, isInstrumentationEvent, targetNames) 627 { 628 var labelNode = document.createElement("label"); 629 labelNode.textContent = name; 630 631 var categoryItem = {}; 632 categoryItem.element = new TreeElement(labelNode); 633 this.categoriesTreeOutline.appendChild(categoryItem.element); 634 categoryItem.element.listItemElement.classList.add("event-category"); 635 categoryItem.element.selectable = true; 636 637 categoryItem.checkbox = this._createCheckbox(labelNode); 638 categoryItem.checkbox.addEventListener("click", this._categoryCheckboxClicked.bind(this, categoryItem), true); 639 640 categoryItem.targetNames = this._stringArrayToLowerCase(targetNames || [WebInspector.EventListenerBreakpointsSidebarPane.eventTargetAny]); 641 categoryItem.children = {}; 642 var category = (isInstrumentationEvent ? WebInspector.EventListenerBreakpointsSidebarPane.categoryInstrumentation : WebInspector.EventListenerBreakpointsSidebarPane.categoryListener); 643 for (var i = 0; i < eventNames.length; ++i) { 644 var eventName = category + eventNames[i]; 645 646 var breakpointItem = {}; 647 var title = WebInspector.EventListenerBreakpointsSidebarPane.eventNameForUI(eventName); 648 649 labelNode = document.createElement("label"); 650 labelNode.textContent = title; 651 652 breakpointItem.element = new TreeElement(labelNode); 653 categoryItem.element.appendChild(breakpointItem.element); 654 655 breakpointItem.element.listItemElement.createChild("div", "breakpoint-hit-marker"); 656 breakpointItem.element.listItemElement.classList.add("source-code"); 657 breakpointItem.element.selectable = false; 658 659 breakpointItem.checkbox = this._createCheckbox(labelNode); 660 breakpointItem.checkbox.addEventListener("click", this._breakpointCheckboxClicked.bind(this, eventName, categoryItem.targetNames), true); 661 breakpointItem.parent = categoryItem; 662 663 categoryItem.children[eventName] = breakpointItem; 664 } 665 this._categoryItems.push(categoryItem); 666 }, 667 668 /** 669 * @param {!Array.<string>} array 670 * @return {!Array.<string>} 671 */ 672 _stringArrayToLowerCase: function(array) 673 { 674 return array.map(function(value) { 675 return value.toLowerCase(); 676 }); 677 }, 678 679 /** 680 * @param {!Element} labelNode 681 * @return {!Element} 682 */ 683 _createCheckbox: function(labelNode) 684 { 685 var checkbox = document.createElementWithClass("input", "checkbox-elem"); 686 checkbox.type = "checkbox"; 687 labelNode.insertBefore(checkbox, labelNode.firstChild); 688 return checkbox; 689 }, 690 691 _categoryCheckboxClicked: function(categoryItem) 692 { 693 var checked = categoryItem.checkbox.checked; 694 for (var eventName in categoryItem.children) { 695 var breakpointItem = categoryItem.children[eventName]; 696 if (breakpointItem.checkbox.checked === checked) 697 continue; 698 if (checked) 699 this._setBreakpoint(eventName, categoryItem.targetNames); 700 else 701 this._removeBreakpoint(eventName, categoryItem.targetNames); 702 } 703 this._saveBreakpoints(); 704 }, 705 706 /** 707 * @param {string} eventName 708 * @param {!Array.<string>} targetNames 709 * @param {!Event} event 710 */ 711 _breakpointCheckboxClicked: function(eventName, targetNames, event) 712 { 713 if (event.target.checked) 714 this._setBreakpoint(eventName, targetNames); 715 else 716 this._removeBreakpoint(eventName, targetNames); 717 this._saveBreakpoints(); 718 }, 719 720 /** 721 * @param {string} eventName 722 * @param {?Array.<string>=} eventTargetNames 723 * @param {!WebInspector.Target=} target 724 */ 725 _setBreakpoint: function(eventName, eventTargetNames, target) 726 { 727 eventTargetNames = eventTargetNames || [WebInspector.EventListenerBreakpointsSidebarPane.eventTargetAny]; 728 for (var i = 0; i < eventTargetNames.length; ++i) { 729 var eventTargetName = eventTargetNames[i]; 730 var breakpointItem = this._findBreakpointItem(eventName, eventTargetName); 731 if (!breakpointItem) 732 continue; 733 breakpointItem.checkbox.checked = true; 734 breakpointItem.parent.dirtyCheckbox = true; 735 this._updateBreakpointOnTarget(eventName, eventTargetName, true, target); 736 } 737 this._updateCategoryCheckboxes(); 738 }, 739 740 /** 741 * @param {string} eventName 742 * @param {?Array.<string>=} eventTargetNames 743 * @param {!WebInspector.Target=} target 744 */ 745 _removeBreakpoint: function(eventName, eventTargetNames, target) 746 { 747 eventTargetNames = eventTargetNames || [WebInspector.EventListenerBreakpointsSidebarPane.eventTargetAny]; 748 for (var i = 0; i < eventTargetNames.length; ++i) { 749 var eventTargetName = eventTargetNames[i]; 750 var breakpointItem = this._findBreakpointItem(eventName, eventTargetName); 751 if (!breakpointItem) 752 continue; 753 breakpointItem.checkbox.checked = false; 754 breakpointItem.parent.dirtyCheckbox = true; 755 this._updateBreakpointOnTarget(eventName, eventTargetName, false, target); 756 } 757 this._updateCategoryCheckboxes(); 758 }, 759 760 /** 761 * @param {string} eventName 762 * @param {string} eventTargetName 763 * @param {boolean} enable 764 * @param {!WebInspector.Target=} target 765 */ 766 _updateBreakpointOnTarget: function(eventName, eventTargetName, enable, target) 767 { 768 var targets = target ? [target] : WebInspector.targetManager.targets(); 769 for (var i = 0; i < targets.length; ++i) { 770 if (eventName.startsWith(WebInspector.EventListenerBreakpointsSidebarPane.categoryListener)) { 771 var protocolEventName = eventName.substring(WebInspector.EventListenerBreakpointsSidebarPane.categoryListener.length); 772 if (enable) 773 targets[i].domdebuggerAgent().setEventListenerBreakpoint(protocolEventName, eventTargetName); 774 else 775 targets[i].domdebuggerAgent().removeEventListenerBreakpoint(protocolEventName, eventTargetName); 776 } else if (eventName.startsWith(WebInspector.EventListenerBreakpointsSidebarPane.categoryInstrumentation)) { 777 var protocolEventName = eventName.substring(WebInspector.EventListenerBreakpointsSidebarPane.categoryInstrumentation.length); 778 if (enable) 779 targets[i].domdebuggerAgent().setInstrumentationBreakpoint(protocolEventName); 780 else 781 targets[i].domdebuggerAgent().removeInstrumentationBreakpoint(protocolEventName); 782 } 783 } 784 }, 785 786 _updateCategoryCheckboxes: function() 787 { 788 for (var i = 0; i < this._categoryItems.length; ++i) { 789 var categoryItem = this._categoryItems[i]; 790 if (!categoryItem.dirtyCheckbox) 791 continue; 792 categoryItem.dirtyCheckbox = false; 793 var hasEnabled = false; 794 var hasDisabled = false; 795 for (var eventName in categoryItem.children) { 796 var breakpointItem = categoryItem.children[eventName]; 797 if (breakpointItem.checkbox.checked) 798 hasEnabled = true; 799 else 800 hasDisabled = true; 801 } 802 categoryItem.checkbox.checked = hasEnabled; 803 categoryItem.checkbox.indeterminate = hasEnabled && hasDisabled; 804 } 805 }, 806 807 /** 808 * @param {string} eventName 809 * @param {string=} targetName 810 * @return {?Object} 811 */ 812 _findBreakpointItem: function(eventName, targetName) 813 { 814 targetName = (targetName || WebInspector.EventListenerBreakpointsSidebarPane.eventTargetAny).toLowerCase(); 815 for (var i = 0; i < this._categoryItems.length; ++i) { 816 var categoryItem = this._categoryItems[i]; 817 if (categoryItem.targetNames.indexOf(targetName) === -1) 818 continue; 819 var breakpointItem = categoryItem.children[eventName]; 820 if (breakpointItem) 821 return breakpointItem; 822 } 823 return null; 824 }, 825 826 /** 827 * @param {string} eventName 828 * @param {string=} targetName 829 */ 830 highlightBreakpoint: function(eventName, targetName) 831 { 832 var breakpointItem = this._findBreakpointItem(eventName, targetName); 833 if (!breakpointItem || !breakpointItem.checkbox.checked) 834 breakpointItem = this._findBreakpointItem(eventName, WebInspector.EventListenerBreakpointsSidebarPane.eventTargetAny); 835 if (!breakpointItem) 836 return; 837 this.expand(); 838 breakpointItem.parent.element.expand(); 839 breakpointItem.element.listItemElement.classList.add("breakpoint-hit"); 840 this._highlightedElement = breakpointItem.element.listItemElement; 841 }, 842 843 clearBreakpointHighlight: function() 844 { 845 if (this._highlightedElement) { 846 this._highlightedElement.classList.remove("breakpoint-hit"); 847 delete this._highlightedElement; 848 } 849 }, 850 851 _saveBreakpoints: function() 852 { 853 var breakpoints = []; 854 for (var i = 0; i < this._categoryItems.length; ++i) { 855 var categoryItem = this._categoryItems[i]; 856 for (var eventName in categoryItem.children) { 857 var breakpointItem = categoryItem.children[eventName]; 858 if (breakpointItem.checkbox.checked) 859 breakpoints.push({ eventName: eventName, targetNames: categoryItem.targetNames }); 860 } 861 } 862 WebInspector.settings.eventListenerBreakpoints.set(breakpoints); 863 }, 864 865 /** 866 * @param {!WebInspector.Target} target 867 */ 868 _restoreBreakpoints: function(target) 869 { 870 var breakpoints = WebInspector.settings.eventListenerBreakpoints.get(); 871 for (var i = 0; i < breakpoints.length; ++i) { 872 var breakpoint = breakpoints[i]; 873 if (breakpoint && typeof breakpoint.eventName === "string") 874 this._setBreakpoint(breakpoint.eventName, breakpoint.targetNames, target); 875 } 876 }, 877 878 __proto__: WebInspector.SidebarPane.prototype 879 } 880