Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / sources / BreakpointsSidebarPane.js
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 }