initial import
[vuplus_webkit] / Source / WebCore / inspector / front-end / 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 WebInspector.JavaScriptBreakpointsSidebarPane = function(model, showSourceLineDelegate)
27 {
28     WebInspector.SidebarPane.call(this, WebInspector.UIString("Breakpoints"));
29
30     this._model = model;
31     this._showSourceLineDelegate = showSourceLineDelegate;
32
33     this.listElement = document.createElement("ol");
34     this.listElement.className = "breakpoint-list";
35
36     this.emptyElement = document.createElement("div");
37     this.emptyElement.className = "info";
38     this.emptyElement.textContent = WebInspector.UIString("No Breakpoints");
39
40     this.bodyElement.appendChild(this.emptyElement);
41
42     this._items = {};
43 }
44
45 WebInspector.JavaScriptBreakpointsSidebarPane.prototype = {
46     addBreakpoint: function(breakpoint)
47     {
48         var breakpointItemId = this._createBreakpointItemId(breakpoint.uiSourceCode, breakpoint.lineNumber);
49         if (breakpointItemId in this._items)
50             return;
51
52         var element = document.createElement("li");
53         element.addStyleClass("cursor-pointer");
54         element.addEventListener("contextmenu", this._contextMenu.bind(this, breakpoint), true);
55         element.addEventListener("click", this._breakpointClicked.bind(this, breakpoint), false);
56
57         var checkbox = document.createElement("input");
58         checkbox.className = "checkbox-elem";
59         checkbox.type = "checkbox";
60         checkbox.checked = breakpoint.enabled;
61         checkbox.addEventListener("click", this._breakpointCheckboxClicked.bind(this, breakpoint), false);
62         element.appendChild(checkbox);
63
64         var url = breakpoint.uiSourceCode.url;
65         var displayName = url ? WebInspector.displayNameForURL(url) : WebInspector.UIString("(program)");
66         var labelElement = document.createTextNode(displayName + ":" + (breakpoint.lineNumber + 1));
67         element.appendChild(labelElement);
68
69         var snippetElement = document.createElement("div");
70         snippetElement.className = "source-text monospace";
71         element.appendChild(snippetElement);
72         function didRequestContent(mimeType, content)
73         {
74             var lineEndings = content.lineEndings();
75             if (breakpoint.lineNumber < lineEndings.length)
76                 snippetElement.textContent = content.substring(lineEndings[breakpoint.lineNumber - 1], lineEndings[breakpoint.lineNumber]);
77         }
78         breakpoint.uiSourceCode.requestContent(didRequestContent.bind(this));
79
80         element._data = breakpoint;
81         var currentElement = this.listElement.firstChild;
82         while (currentElement) {
83             if (currentElement._data && this._compareBreakpoints(currentElement._data, element._data) > 0)
84                 break;
85             currentElement = currentElement.nextSibling;
86         }
87         this._addListElement(element, currentElement);
88
89         var breakpointItem = {};
90         breakpointItem.element = element;
91         breakpointItem.checkbox = checkbox;
92         this._items[breakpointItemId] = breakpointItem;
93
94         if (!this.expanded)
95             this.expanded = true;
96     },
97
98     removeBreakpoint: function(uiSourceCode, lineNumber)
99     {
100         var breakpointItemId = this._createBreakpointItemId(uiSourceCode, lineNumber);
101         var breakpointItem = this._items[breakpointItemId];
102         if (!breakpointItem)
103             return;
104         delete this._items[breakpointItemId];
105         this._removeListElement(breakpointItem.element);
106     },
107
108     highlightBreakpoint: function(uiSourceCode, lineNumber)
109     {
110         var breakpointItem = this._items[this._createBreakpointItemId(uiSourceCode, lineNumber)];
111         if (!breakpointItem)
112             return;
113         breakpointItem.element.addStyleClass("breakpoint-hit");
114         this._highlightedBreakpointItem = breakpointItem;
115     },
116
117     clearBreakpointHighlight: function()
118     {
119         if (this._highlightedBreakpointItem) {
120             this._highlightedBreakpointItem.element.removeStyleClass("breakpoint-hit");
121             delete this._highlightedBreakpointItem;
122         }
123     },
124
125     _createBreakpointItemId: function(uiSourceCode, lineNumber)
126     {
127         return uiSourceCode.id + ":" + lineNumber;
128     },
129
130     _breakpointClicked: function(breakpoint, event)
131     {
132         this._showSourceLineDelegate(breakpoint.uiSourceCode, breakpoint.lineNumber);
133     },
134
135     _breakpointCheckboxClicked: function(breakpoint, event)
136     {
137         // Breakpoint element has it's own click handler.
138         event.stopPropagation();
139
140         this._model.setBreakpointEnabled(breakpoint.uiSourceCode, breakpoint.lineNumber, event.target.checked);
141     },
142
143     _contextMenu: function(breakpoint, event)
144     {
145         var contextMenu = new WebInspector.ContextMenu();
146
147         var removeHandler = this._model.removeBreakpoint.bind(this._model, breakpoint.uiSourceCode, breakpoint.lineNumber);
148         contextMenu.appendItem(WebInspector.UIString("Remove Breakpoint"), removeHandler);
149
150         contextMenu.show(event);
151     },
152
153     _addListElement: function(element, beforeElement)
154     {
155         if (beforeElement)
156             this.listElement.insertBefore(element, beforeElement);
157         else {
158             if (!this.listElement.firstChild) {
159                 this.bodyElement.removeChild(this.emptyElement);
160                 this.bodyElement.appendChild(this.listElement);
161             }
162             this.listElement.appendChild(element);
163         }
164     },
165
166     _removeListElement: function(element)
167     {
168         this.listElement.removeChild(element);
169         if (!this.listElement.firstChild) {
170             this.bodyElement.removeChild(this.listElement);
171             this.bodyElement.appendChild(this.emptyElement);
172         }
173     },
174
175     _compare: function(x, y)
176     {
177         if (x !== y)
178             return x < y ? -1 : 1;
179         return 0;
180     },
181
182     _compareBreakpoints: function(b1, b2)
183     {
184         return this._compare(b1.url, b2.url) || this._compare(b1.lineNumber, b2.lineNumber);
185     },
186
187     reset: function()
188     {
189         this.listElement.removeChildren();
190         if (this.listElement.parentElement) {
191             this.bodyElement.removeChild(this.listElement);
192             this.bodyElement.appendChild(this.emptyElement);
193         }
194         this._items = {};
195     }
196 }
197
198 WebInspector.JavaScriptBreakpointsSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype;
199
200 WebInspector.NativeBreakpointsSidebarPane = function(title)
201 {
202     WebInspector.SidebarPane.call(this, title);
203
204     this.listElement = document.createElement("ol");
205     this.listElement.className = "breakpoint-list";
206
207     this.emptyElement = document.createElement("div");
208     this.emptyElement.className = "info";
209     this.emptyElement.textContent = WebInspector.UIString("No Breakpoints");
210
211     this.bodyElement.appendChild(this.emptyElement);
212 }
213
214 WebInspector.NativeBreakpointsSidebarPane.prototype = {
215     _addListElement: function(element, beforeElement)
216     {
217         if (beforeElement)
218             this.listElement.insertBefore(element, beforeElement);
219         else {
220             if (!this.listElement.firstChild) {
221                 this.bodyElement.removeChild(this.emptyElement);
222                 this.bodyElement.appendChild(this.listElement);
223             }
224             this.listElement.appendChild(element);
225         }
226     },
227
228     _removeListElement: function(element)
229     {
230         this.listElement.removeChild(element);
231         if (!this.listElement.firstChild) {
232             this.bodyElement.removeChild(this.listElement);
233             this.bodyElement.appendChild(this.emptyElement);
234         }
235     },
236
237     _reset: function()
238     {
239         this.listElement.removeChildren();
240         if (this.listElement.parentElement) {
241             this.bodyElement.removeChild(this.listElement);
242             this.bodyElement.appendChild(this.emptyElement);
243         }
244     }
245 }
246
247 WebInspector.NativeBreakpointsSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype;
248
249 WebInspector.XHRBreakpointsSidebarPane = function()
250 {
251     WebInspector.NativeBreakpointsSidebarPane.call(this, WebInspector.UIString("XHR Breakpoints"));
252
253     this._breakpointElements = {};
254
255     var addButton = document.createElement("button");
256     addButton.className = "pane-title-button add";
257     addButton.addEventListener("click", this._addButtonClicked.bind(this), false);
258     this.titleElement.appendChild(addButton);
259
260     this._restoreBreakpoints();
261 }
262
263 WebInspector.XHRBreakpointsSidebarPane.prototype = {
264     _addButtonClicked: function(event)
265     {
266         event.stopPropagation();
267
268         this.expanded = true;
269
270         var inputElementContainer = document.createElement("p");
271         inputElementContainer.className = "breakpoint-condition";
272         var inputElement = document.createElement("span");
273         inputElementContainer.textContent = WebInspector.UIString("Break when URL contains:");
274         inputElement.className = "editing";
275         inputElement.id = "breakpoint-condition-input";
276         inputElementContainer.appendChild(inputElement);
277         this._addListElement(inputElementContainer, this.listElement.firstChild);
278
279         function finishEditing(accept, e, text)
280         {
281             this._removeListElement(inputElementContainer);
282             if (accept) {
283                 this._setBreakpoint(text, true);
284                 this._saveBreakpoints();
285             }
286         }
287
288         WebInspector.startEditing(inputElement, {
289             commitHandler: finishEditing.bind(this, true),
290             cancelHandler: finishEditing.bind(this, false)
291         });
292     },
293
294     _setBreakpoint: function(url, enabled)
295     {
296         if (url in this._breakpointElements)
297             return;
298
299         var element = document.createElement("li");
300         element._url = url;
301         element.addEventListener("contextmenu", this._contextMenu.bind(this, url), true);
302
303         var checkboxElement = document.createElement("input");
304         checkboxElement.className = "checkbox-elem";
305         checkboxElement.type = "checkbox";
306         checkboxElement.checked = enabled;
307         checkboxElement.addEventListener("click", this._checkboxClicked.bind(this, url), false);
308         element._checkboxElement = checkboxElement;
309         element.appendChild(checkboxElement);
310
311         var labelElement = document.createElement("span");
312         if (!url)
313             labelElement.textContent = WebInspector.UIString("Any XHR");
314         else
315             labelElement.textContent = WebInspector.UIString("URL contains \"%s\"", url);
316         labelElement.addStyleClass("cursor-auto");
317         labelElement.addEventListener("dblclick", this._labelClicked.bind(this, url), false);
318         element.appendChild(labelElement);
319
320         var currentElement = this.listElement.firstChild;
321         while (currentElement) {
322             if (currentElement._url && currentElement._url < element._url)
323                 break;
324             currentElement = currentElement.nextSibling;
325         }
326         this._addListElement(element, currentElement);
327         this._breakpointElements[url] = element;
328         if (enabled)
329             DOMDebuggerAgent.setXHRBreakpoint(url);
330     },
331
332     _removeBreakpoint: function(url)
333     {
334         var element = this._breakpointElements[url];
335         if (!element)
336             return;
337
338         this._removeListElement(element);
339         delete this._breakpointElements[url];
340         if (element._checkboxElement.checked)
341             DOMDebuggerAgent.removeXHRBreakpoint(url);
342     },
343
344     _contextMenu: function(url, event)
345     {
346         var contextMenu = new WebInspector.ContextMenu();
347         function removeBreakpoint()
348         {
349             this._removeBreakpoint(url);
350             this._saveBreakpoints();
351         }
352         contextMenu.appendItem(WebInspector.UIString("Remove Breakpoint"), removeBreakpoint.bind(this));
353         contextMenu.show(event);
354     },
355
356     _checkboxClicked: function(url, event)
357     {
358         if (event.target.checked)
359             DOMDebuggerAgent.setXHRBreakpoint(url);
360         else
361             DOMDebuggerAgent.removeXHRBreakpoint(url);
362         this._saveBreakpoints();
363     },
364
365     _labelClicked: function(url)
366     {
367         var element = this._breakpointElements[url];
368         var inputElement = document.createElement("span");
369         inputElement.className = "breakpoint-condition editing";
370         inputElement.textContent = url;
371         this.listElement.insertBefore(inputElement, element);
372         element.addStyleClass("hidden");
373
374         function finishEditing(accept, e, text)
375         {
376             this._removeListElement(inputElement);
377             if (accept) {
378                 this._removeBreakpoint(url);
379                 this._setBreakpoint(text, element._checkboxElement.checked);
380                 this._saveBreakpoints();
381             } else
382                 element.removeStyleClass("hidden");
383         }
384
385         WebInspector.startEditing(inputElement, {
386             commitHandler: finishEditing.bind(this, true),
387             cancelHandler: finishEditing.bind(this, false)
388         });
389     },
390
391     highlightBreakpoint: function(url)
392     {
393         var element = this._breakpointElements[url];
394         if (!element)
395             return;
396         this.expanded = true;
397         element.addStyleClass("breakpoint-hit");
398         this._highlightedElement = element;
399     },
400
401     clearBreakpointHighlight: function()
402     {
403         if (this._highlightedElement) {
404             this._highlightedElement.removeStyleClass("breakpoint-hit");
405             delete this._highlightedElement;
406         }
407     },
408
409     _saveBreakpoints: function()
410     {
411         var breakpoints = [];
412         for (var url in this._breakpointElements)
413             breakpoints.push({ url: url, enabled: this._breakpointElements[url]._checkboxElement.checked });
414         WebInspector.settings.xhrBreakpoints.set(breakpoints);
415     },
416
417     _restoreBreakpoints: function()
418     {
419         var breakpoints = WebInspector.settings.xhrBreakpoints.get();
420         for (var i = 0; i < breakpoints.length; ++i) {
421             var breakpoint = breakpoints[i];
422             if (breakpoint && typeof breakpoint.url === "string")
423                 this._setBreakpoint(breakpoint.url, breakpoint.enabled);
424         }
425     }
426 }
427
428 WebInspector.XHRBreakpointsSidebarPane.prototype.__proto__ = WebInspector.NativeBreakpointsSidebarPane.prototype;
429
430 WebInspector.EventListenerBreakpointsSidebarPane = function()
431 {
432     WebInspector.SidebarPane.call(this, WebInspector.UIString("Event Listener Breakpoints"));
433
434     this.categoriesElement = document.createElement("ol");
435     this.categoriesElement.tabIndex = 0;
436     this.categoriesElement.addStyleClass("properties-tree");
437     this.categoriesElement.addStyleClass("event-listener-breakpoints");
438     this.categoriesTreeOutline = new TreeOutline(this.categoriesElement);
439     this.bodyElement.appendChild(this.categoriesElement);
440
441     this._breakpointItems = {};
442     this._createCategory(WebInspector.UIString("Keyboard"), "listener", ["keydown", "keyup", "keypress", "textInput"]);
443     this._createCategory(WebInspector.UIString("Mouse"), "listener", ["click", "dblclick", "mousedown", "mouseup", "mouseover", "mousemove", "mouseout", "mousewheel"]);
444     // FIXME: uncomment following once inspector stops being drop targer in major ports.
445     // Otherwise, inspector page reacts on drop event and tries to load the event data.
446     // this._createCategory(WebInspector.UIString("Drag"), "listener", ["drag", "drop", "dragstart", "dragend", "dragenter", "dragleave", "dragover"]);
447     this._createCategory(WebInspector.UIString("Control"), "listener", ["resize", "scroll", "zoom", "focus", "blur", "select", "change", "submit", "reset"]);
448     this._createCategory(WebInspector.UIString("Clipboard"), "listener", ["copy", "cut", "paste", "beforecopy", "beforecut", "beforepaste"]);
449     this._createCategory(WebInspector.UIString("Load"), "listener", ["load", "unload", "abort", "error"]);
450     this._createCategory(WebInspector.UIString("DOM Mutation"), "listener", ["DOMActivate", "DOMFocusIn", "DOMFocusOut", "DOMAttrModified", "DOMCharacterDataModified", "DOMNodeInserted", "DOMNodeInsertedIntoDocument", "DOMNodeRemoved", "DOMNodeRemovedFromDocument", "DOMSubtreeModified", "DOMContentLoaded"]);
451     this._createCategory(WebInspector.UIString("Device"), "listener", ["deviceorientation", "devicemotion"]);
452     this._createCategory(WebInspector.UIString("Timer"), "instrumentation", ["setTimer", "clearTimer", "timerFired"]);
453
454     this._restoreBreakpoints();
455 }
456
457 WebInspector.EventListenerBreakpointsSidebarPane.eventNameForUI = function(eventName)
458 {
459     if (!WebInspector.EventListenerBreakpointsSidebarPane._eventNamesForUI) {
460         WebInspector.EventListenerBreakpointsSidebarPane._eventNamesForUI = {
461             "instrumentation:setTimer": WebInspector.UIString("Set Timer"),
462             "instrumentation:clearTimer": WebInspector.UIString("Clear Timer"),
463             "instrumentation:timerFired": WebInspector.UIString("Timer Fired")
464         };
465     }
466     return WebInspector.EventListenerBreakpointsSidebarPane._eventNamesForUI[eventName] || eventName.substring(eventName.indexOf(":") + 1);
467 }
468
469 WebInspector.EventListenerBreakpointsSidebarPane.prototype = {
470     _createCategory: function(name, type, eventNames)
471     {
472         var categoryItem = {};
473         categoryItem.element = new TreeElement(name);
474         this.categoriesTreeOutline.appendChild(categoryItem.element);
475         categoryItem.element.listItemElement.addStyleClass("event-category");
476         categoryItem.element.selectable = true;
477
478         categoryItem.checkbox = this._createCheckbox(categoryItem.element);
479         categoryItem.checkbox.addEventListener("click", this._categoryCheckboxClicked.bind(this, categoryItem), true);
480
481         categoryItem.children = {};
482         for (var i = 0; i < eventNames.length; ++i) {
483             var eventName = type + ":" + eventNames[i];
484
485             var breakpointItem = {};
486             var title = WebInspector.EventListenerBreakpointsSidebarPane.eventNameForUI(eventName);
487             breakpointItem.element = new TreeElement(title);
488             categoryItem.element.appendChild(breakpointItem.element);
489             var hitMarker = document.createElement("div");
490             hitMarker.className = "breakpoint-hit-marker";
491             breakpointItem.element.listItemElement.appendChild(hitMarker);
492             breakpointItem.element.listItemElement.addStyleClass("source-code");
493             breakpointItem.element.selectable = true;
494
495             breakpointItem.checkbox = this._createCheckbox(breakpointItem.element);
496             breakpointItem.checkbox.addEventListener("click", this._breakpointCheckboxClicked.bind(this, eventName), true);
497             breakpointItem.parent = categoryItem;
498
499             this._breakpointItems[eventName] = breakpointItem;
500             categoryItem.children[eventName] = breakpointItem;
501         }
502     },
503
504     _createCheckbox: function(treeElement)
505     {
506         var checkbox = document.createElement("input");
507         checkbox.className = "checkbox-elem";
508         checkbox.type = "checkbox";
509         treeElement.listItemElement.insertBefore(checkbox, treeElement.listItemElement.firstChild);
510         return checkbox;
511     },
512
513     _categoryCheckboxClicked: function(categoryItem)
514     {
515         var checked = categoryItem.checkbox.checked;
516         for (var eventName in categoryItem.children) {
517             var breakpointItem = categoryItem.children[eventName];
518             if (breakpointItem.checkbox.checked === checked)
519                 continue;
520             if (checked)
521                 this._setBreakpoint(eventName);
522             else
523                 this._removeBreakpoint(eventName);
524         }
525         this._saveBreakpoints();
526     },
527
528     _breakpointCheckboxClicked: function(eventName, event)
529     {
530         if (event.target.checked)
531             this._setBreakpoint(eventName);
532         else
533             this._removeBreakpoint(eventName);
534         this._saveBreakpoints();
535     },
536
537     _setBreakpoint: function(eventName)
538     {
539         var breakpointItem = this._breakpointItems[eventName];
540         if (!breakpointItem)
541             return;
542         breakpointItem.checkbox.checked = true;
543         DOMDebuggerAgent.setEventListenerBreakpoint(eventName);
544         this._updateCategoryCheckbox(breakpointItem.parent);
545     },
546
547     _removeBreakpoint: function(eventName)
548     {
549         var breakpointItem = this._breakpointItems[eventName];
550         if (!breakpointItem)
551             return;
552         breakpointItem.checkbox.checked = false;
553         DOMDebuggerAgent.removeEventListenerBreakpoint(eventName);
554         this._updateCategoryCheckbox(breakpointItem.parent);
555     },
556
557     _updateCategoryCheckbox: function(categoryItem)
558     {
559         var hasEnabled = false, hasDisabled = false;
560         for (var eventName in categoryItem.children) {
561             var breakpointItem = categoryItem.children[eventName];
562             if (breakpointItem.checkbox.checked)
563                 hasEnabled = true;
564             else
565                 hasDisabled = true;
566         }
567         categoryItem.checkbox.checked = hasEnabled;
568         categoryItem.checkbox.indeterminate = hasEnabled && hasDisabled;
569     },
570
571     highlightBreakpoint: function(eventName)
572     {
573         var breakpointItem = this._breakpointItems[eventName];
574         if (!breakpointItem)
575             return;
576         this.expanded = true;
577         breakpointItem.parent.element.expand();
578         breakpointItem.element.listItemElement.addStyleClass("breakpoint-hit");
579         this._highlightedElement = breakpointItem.element.listItemElement;
580     },
581
582     clearBreakpointHighlight: function()
583     {
584         if (this._highlightedElement) {
585             this._highlightedElement.removeStyleClass("breakpoint-hit");
586             delete this._highlightedElement;
587         }
588     },
589
590     _saveBreakpoints: function()
591     {
592         var breakpoints = [];
593         for (var eventName in this._breakpointItems) {
594             if (this._breakpointItems[eventName].checkbox.checked)
595                 breakpoints.push({ eventName: eventName });
596         }
597         WebInspector.settings.eventListenerBreakpoints.set(breakpoints);
598     },
599
600     _restoreBreakpoints: function()
601     {
602         var breakpoints = WebInspector.settings.eventListenerBreakpoints.get();
603         for (var i = 0; i < breakpoints.length; ++i) {
604             var breakpoint = breakpoints[i];
605             if (breakpoint && typeof breakpoint.eventName === "string")
606                 this._setBreakpoint(breakpoint.eventName);
607         }
608     }
609 }
610
611 WebInspector.EventListenerBreakpointsSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype;