2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
26 WebInspector.JavaScriptBreakpointsSidebarPane = function(model, showSourceLineDelegate)
28 WebInspector.SidebarPane.call(this, WebInspector.UIString("Breakpoints"));
31 this._showSourceLineDelegate = showSourceLineDelegate;
33 this.listElement = document.createElement("ol");
34 this.listElement.className = "breakpoint-list";
36 this.emptyElement = document.createElement("div");
37 this.emptyElement.className = "info";
38 this.emptyElement.textContent = WebInspector.UIString("No Breakpoints");
40 this.bodyElement.appendChild(this.emptyElement);
45 WebInspector.JavaScriptBreakpointsSidebarPane.prototype = {
46 addBreakpoint: function(breakpoint)
48 var breakpointItemId = this._createBreakpointItemId(breakpoint.uiSourceCode, breakpoint.lineNumber);
49 if (breakpointItemId in this._items)
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);
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);
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);
69 var snippetElement = document.createElement("div");
70 snippetElement.className = "source-text monospace";
71 element.appendChild(snippetElement);
72 function didRequestContent(mimeType, content)
74 var lineEndings = content.lineEndings();
75 if (breakpoint.lineNumber < lineEndings.length)
76 snippetElement.textContent = content.substring(lineEndings[breakpoint.lineNumber - 1], lineEndings[breakpoint.lineNumber]);
78 breakpoint.uiSourceCode.requestContent(didRequestContent.bind(this));
80 element._data = breakpoint;
81 var currentElement = this.listElement.firstChild;
82 while (currentElement) {
83 if (currentElement._data && this._compareBreakpoints(currentElement._data, element._data) > 0)
85 currentElement = currentElement.nextSibling;
87 this._addListElement(element, currentElement);
89 var breakpointItem = {};
90 breakpointItem.element = element;
91 breakpointItem.checkbox = checkbox;
92 this._items[breakpointItemId] = breakpointItem;
98 removeBreakpoint: function(uiSourceCode, lineNumber)
100 var breakpointItemId = this._createBreakpointItemId(uiSourceCode, lineNumber);
101 var breakpointItem = this._items[breakpointItemId];
104 delete this._items[breakpointItemId];
105 this._removeListElement(breakpointItem.element);
108 highlightBreakpoint: function(uiSourceCode, lineNumber)
110 var breakpointItem = this._items[this._createBreakpointItemId(uiSourceCode, lineNumber)];
113 breakpointItem.element.addStyleClass("breakpoint-hit");
114 this._highlightedBreakpointItem = breakpointItem;
117 clearBreakpointHighlight: function()
119 if (this._highlightedBreakpointItem) {
120 this._highlightedBreakpointItem.element.removeStyleClass("breakpoint-hit");
121 delete this._highlightedBreakpointItem;
125 _createBreakpointItemId: function(uiSourceCode, lineNumber)
127 return uiSourceCode.id + ":" + lineNumber;
130 _breakpointClicked: function(breakpoint, event)
132 this._showSourceLineDelegate(breakpoint.uiSourceCode, breakpoint.lineNumber);
135 _breakpointCheckboxClicked: function(breakpoint, event)
137 // Breakpoint element has it's own click handler.
138 event.stopPropagation();
140 this._model.setBreakpointEnabled(breakpoint.uiSourceCode, breakpoint.lineNumber, event.target.checked);
143 _contextMenu: function(breakpoint, event)
145 var contextMenu = new WebInspector.ContextMenu();
147 var removeHandler = this._model.removeBreakpoint.bind(this._model, breakpoint.uiSourceCode, breakpoint.lineNumber);
148 contextMenu.appendItem(WebInspector.UIString("Remove Breakpoint"), removeHandler);
150 contextMenu.show(event);
153 _addListElement: function(element, beforeElement)
156 this.listElement.insertBefore(element, beforeElement);
158 if (!this.listElement.firstChild) {
159 this.bodyElement.removeChild(this.emptyElement);
160 this.bodyElement.appendChild(this.listElement);
162 this.listElement.appendChild(element);
166 _removeListElement: function(element)
168 this.listElement.removeChild(element);
169 if (!this.listElement.firstChild) {
170 this.bodyElement.removeChild(this.listElement);
171 this.bodyElement.appendChild(this.emptyElement);
175 _compare: function(x, y)
178 return x < y ? -1 : 1;
182 _compareBreakpoints: function(b1, b2)
184 return this._compare(b1.url, b2.url) || this._compare(b1.lineNumber, b2.lineNumber);
189 this.listElement.removeChildren();
190 if (this.listElement.parentElement) {
191 this.bodyElement.removeChild(this.listElement);
192 this.bodyElement.appendChild(this.emptyElement);
198 WebInspector.JavaScriptBreakpointsSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype;
200 WebInspector.NativeBreakpointsSidebarPane = function(title)
202 WebInspector.SidebarPane.call(this, title);
204 this.listElement = document.createElement("ol");
205 this.listElement.className = "breakpoint-list";
207 this.emptyElement = document.createElement("div");
208 this.emptyElement.className = "info";
209 this.emptyElement.textContent = WebInspector.UIString("No Breakpoints");
211 this.bodyElement.appendChild(this.emptyElement);
214 WebInspector.NativeBreakpointsSidebarPane.prototype = {
215 _addListElement: function(element, beforeElement)
218 this.listElement.insertBefore(element, beforeElement);
220 if (!this.listElement.firstChild) {
221 this.bodyElement.removeChild(this.emptyElement);
222 this.bodyElement.appendChild(this.listElement);
224 this.listElement.appendChild(element);
228 _removeListElement: function(element)
230 this.listElement.removeChild(element);
231 if (!this.listElement.firstChild) {
232 this.bodyElement.removeChild(this.listElement);
233 this.bodyElement.appendChild(this.emptyElement);
239 this.listElement.removeChildren();
240 if (this.listElement.parentElement) {
241 this.bodyElement.removeChild(this.listElement);
242 this.bodyElement.appendChild(this.emptyElement);
247 WebInspector.NativeBreakpointsSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype;
249 WebInspector.XHRBreakpointsSidebarPane = function()
251 WebInspector.NativeBreakpointsSidebarPane.call(this, WebInspector.UIString("XHR Breakpoints"));
253 this._breakpointElements = {};
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);
260 this._restoreBreakpoints();
263 WebInspector.XHRBreakpointsSidebarPane.prototype = {
264 _addButtonClicked: function(event)
266 event.stopPropagation();
268 this.expanded = true;
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);
279 function finishEditing(accept, e, text)
281 this._removeListElement(inputElementContainer);
283 this._setBreakpoint(text, true);
284 this._saveBreakpoints();
288 WebInspector.startEditing(inputElement, {
289 commitHandler: finishEditing.bind(this, true),
290 cancelHandler: finishEditing.bind(this, false)
294 _setBreakpoint: function(url, enabled)
296 if (url in this._breakpointElements)
299 var element = document.createElement("li");
301 element.addEventListener("contextmenu", this._contextMenu.bind(this, url), true);
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);
311 var labelElement = document.createElement("span");
313 labelElement.textContent = WebInspector.UIString("Any XHR");
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);
320 var currentElement = this.listElement.firstChild;
321 while (currentElement) {
322 if (currentElement._url && currentElement._url < element._url)
324 currentElement = currentElement.nextSibling;
326 this._addListElement(element, currentElement);
327 this._breakpointElements[url] = element;
329 DOMDebuggerAgent.setXHRBreakpoint(url);
332 _removeBreakpoint: function(url)
334 var element = this._breakpointElements[url];
338 this._removeListElement(element);
339 delete this._breakpointElements[url];
340 if (element._checkboxElement.checked)
341 DOMDebuggerAgent.removeXHRBreakpoint(url);
344 _contextMenu: function(url, event)
346 var contextMenu = new WebInspector.ContextMenu();
347 function removeBreakpoint()
349 this._removeBreakpoint(url);
350 this._saveBreakpoints();
352 contextMenu.appendItem(WebInspector.UIString("Remove Breakpoint"), removeBreakpoint.bind(this));
353 contextMenu.show(event);
356 _checkboxClicked: function(url, event)
358 if (event.target.checked)
359 DOMDebuggerAgent.setXHRBreakpoint(url);
361 DOMDebuggerAgent.removeXHRBreakpoint(url);
362 this._saveBreakpoints();
365 _labelClicked: function(url)
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");
374 function finishEditing(accept, e, text)
376 this._removeListElement(inputElement);
378 this._removeBreakpoint(url);
379 this._setBreakpoint(text, element._checkboxElement.checked);
380 this._saveBreakpoints();
382 element.removeStyleClass("hidden");
385 WebInspector.startEditing(inputElement, {
386 commitHandler: finishEditing.bind(this, true),
387 cancelHandler: finishEditing.bind(this, false)
391 highlightBreakpoint: function(url)
393 var element = this._breakpointElements[url];
396 this.expanded = true;
397 element.addStyleClass("breakpoint-hit");
398 this._highlightedElement = element;
401 clearBreakpointHighlight: function()
403 if (this._highlightedElement) {
404 this._highlightedElement.removeStyleClass("breakpoint-hit");
405 delete this._highlightedElement;
409 _saveBreakpoints: function()
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);
417 _restoreBreakpoints: function()
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);
428 WebInspector.XHRBreakpointsSidebarPane.prototype.__proto__ = WebInspector.NativeBreakpointsSidebarPane.prototype;
430 WebInspector.EventListenerBreakpointsSidebarPane = function()
432 WebInspector.SidebarPane.call(this, WebInspector.UIString("Event Listener Breakpoints"));
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);
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"]);
454 this._restoreBreakpoints();
457 WebInspector.EventListenerBreakpointsSidebarPane.eventNameForUI = function(eventName)
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")
466 return WebInspector.EventListenerBreakpointsSidebarPane._eventNamesForUI[eventName] || eventName.substring(eventName.indexOf(":") + 1);
469 WebInspector.EventListenerBreakpointsSidebarPane.prototype = {
470 _createCategory: function(name, type, eventNames)
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;
478 categoryItem.checkbox = this._createCheckbox(categoryItem.element);
479 categoryItem.checkbox.addEventListener("click", this._categoryCheckboxClicked.bind(this, categoryItem), true);
481 categoryItem.children = {};
482 for (var i = 0; i < eventNames.length; ++i) {
483 var eventName = type + ":" + eventNames[i];
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;
495 breakpointItem.checkbox = this._createCheckbox(breakpointItem.element);
496 breakpointItem.checkbox.addEventListener("click", this._breakpointCheckboxClicked.bind(this, eventName), true);
497 breakpointItem.parent = categoryItem;
499 this._breakpointItems[eventName] = breakpointItem;
500 categoryItem.children[eventName] = breakpointItem;
504 _createCheckbox: function(treeElement)
506 var checkbox = document.createElement("input");
507 checkbox.className = "checkbox-elem";
508 checkbox.type = "checkbox";
509 treeElement.listItemElement.insertBefore(checkbox, treeElement.listItemElement.firstChild);
513 _categoryCheckboxClicked: function(categoryItem)
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)
521 this._setBreakpoint(eventName);
523 this._removeBreakpoint(eventName);
525 this._saveBreakpoints();
528 _breakpointCheckboxClicked: function(eventName, event)
530 if (event.target.checked)
531 this._setBreakpoint(eventName);
533 this._removeBreakpoint(eventName);
534 this._saveBreakpoints();
537 _setBreakpoint: function(eventName)
539 var breakpointItem = this._breakpointItems[eventName];
542 breakpointItem.checkbox.checked = true;
543 DOMDebuggerAgent.setEventListenerBreakpoint(eventName);
544 this._updateCategoryCheckbox(breakpointItem.parent);
547 _removeBreakpoint: function(eventName)
549 var breakpointItem = this._breakpointItems[eventName];
552 breakpointItem.checkbox.checked = false;
553 DOMDebuggerAgent.removeEventListenerBreakpoint(eventName);
554 this._updateCategoryCheckbox(breakpointItem.parent);
557 _updateCategoryCheckbox: function(categoryItem)
559 var hasEnabled = false, hasDisabled = false;
560 for (var eventName in categoryItem.children) {
561 var breakpointItem = categoryItem.children[eventName];
562 if (breakpointItem.checkbox.checked)
567 categoryItem.checkbox.checked = hasEnabled;
568 categoryItem.checkbox.indeterminate = hasEnabled && hasDisabled;
571 highlightBreakpoint: function(eventName)
573 var breakpointItem = this._breakpointItems[eventName];
576 this.expanded = true;
577 breakpointItem.parent.element.expand();
578 breakpointItem.element.listItemElement.addStyleClass("breakpoint-hit");
579 this._highlightedElement = breakpointItem.element.listItemElement;
582 clearBreakpointHighlight: function()
584 if (this._highlightedElement) {
585 this._highlightedElement.removeStyleClass("breakpoint-hit");
586 delete this._highlightedElement;
590 _saveBreakpoints: function()
592 var breakpoints = [];
593 for (var eventName in this._breakpointItems) {
594 if (this._breakpointItems[eventName].checkbox.checked)
595 breakpoints.push({ eventName: eventName });
597 WebInspector.settings.eventListenerBreakpoints.set(breakpoints);
600 _restoreBreakpoints: function()
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);
611 WebInspector.EventListenerBreakpointsSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype;