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.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