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