• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
33WebInspector.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
63WebInspector.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 */
291WebInspector.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
308WebInspector.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 */
512WebInspector.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
545WebInspector.EventListenerBreakpointsSidebarPane.categoryListener = "listener:";
546WebInspector.EventListenerBreakpointsSidebarPane.categoryInstrumentation = "instrumentation:";
547WebInspector.EventListenerBreakpointsSidebarPane.eventTargetAny = "*";
548
549/**
550 * @param {string} eventName
551 * @param {!Object=} auxData
552 * @return {string}
553 */
554WebInspector.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
579WebInspector.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