2 * Copyright (C) 2013 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. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
26 WebInspector.Breakpoint = function(sourceCodeLocationOrInfo, disabled, condition)
28 WebInspector.Object.call(this);
30 if (sourceCodeLocationOrInfo instanceof WebInspector.SourceCodeLocation) {
31 var sourceCode = sourceCodeLocationOrInfo.sourceCode;
32 var url = sourceCode ? sourceCode.url : null;
33 var scriptIdentifier = sourceCode instanceof WebInspector.Script ? sourceCode.id : null;
34 var location = sourceCodeLocationOrInfo;
35 } else if (sourceCodeLocationOrInfo && typeof sourceCodeLocationOrInfo === "object") {
36 var url = sourceCodeLocationOrInfo.url;
37 var lineNumber = sourceCodeLocationOrInfo.lineNumber || 0;
38 var columnNumber = sourceCodeLocationOrInfo.columnNumber || 0;
39 var location = new WebInspector.SourceCodeLocation(null, lineNumber, columnNumber);
40 var autoContinue = sourceCodeLocationOrInfo.autoContinue || false;
41 var actions = sourceCodeLocationOrInfo.actions || [];
42 for (var i = 0; i < actions.length; ++i)
43 actions[i] = new WebInspector.BreakpointAction(this, actions[i]);
44 disabled = sourceCodeLocationOrInfo.disabled;
45 condition = sourceCodeLocationOrInfo.condition;
47 console.error("Unexpected type passed to WebInspector.Breakpoint", sourceCodeLocationOrInfo);
50 this._url = url || null;
51 this._scriptIdentifier = scriptIdentifier || null;
52 this._disabled = disabled || false;
53 this._condition = condition || "";
54 this._autoContinue = autoContinue || false;
55 this._actions = actions || [];
56 this._resolved = false;
58 this._sourceCodeLocation = location;
59 this._sourceCodeLocation.addEventListener(WebInspector.SourceCodeLocation.Event.LocationChanged, this._sourceCodeLocationLocationChanged, this);
60 this._sourceCodeLocation.addEventListener(WebInspector.SourceCodeLocation.Event.DisplayLocationChanged, this._sourceCodeLocationDisplayLocationChanged, this);
63 WebInspector.Object.addConstructorFunctions(WebInspector.Breakpoint);
65 WebInspector.Breakpoint.PopoverClassName = "edit-breakpoint-popover-content";
66 WebInspector.Breakpoint.WidePopoverClassName = "wide";
67 WebInspector.Breakpoint.PopoverConditionInputId = "edit-breakpoint-popover-condition";
68 WebInspector.Breakpoint.PopoverOptionsAutoContinueInputId = "edit-breakpoint-popoover-auto-continue";
70 WebInspector.Breakpoint.DefaultBreakpointActionType = WebInspector.BreakpointAction.Type.Log;
72 WebInspector.Breakpoint.Event = {
73 DisabledStateDidChange: "breakpoint-disabled-state-did-change",
74 ResolvedStateDidChange: "breakpoint-resolved-state-did-change",
75 ConditionDidChange: "breakpoint-condition-did-change",
76 ActionsDidChange: "breakpoint-actions-did-change",
77 AutoContinueDidChange: "breakpoint-auto-continue-did-change",
78 LocationDidChange: "breakpoint-location-did-change",
79 DisplayLocationDidChange: "breakpoint-display-location-did-change",
82 WebInspector.Breakpoint.prototype = {
83 constructor: WebInspector.Breakpoint,
94 this._id = id || null;
102 get scriptIdentifier()
104 return this._scriptIdentifier;
107 get sourceCodeLocation()
109 return this._sourceCodeLocation;
114 return this._resolved && WebInspector.debuggerManager.breakpointsEnabled;
117 set resolved(resolved)
119 if (this._resolved === resolved)
122 this._resolved = resolved || false;
124 this.dispatchEventToListeners(WebInspector.Breakpoint.Event.ResolvedStateDidChange);
129 return this._disabled;
132 set disabled(disabled)
134 if (this._disabled === disabled)
137 this._disabled = disabled || false;
139 this.dispatchEventToListeners(WebInspector.Breakpoint.Event.DisabledStateDidChange);
144 return this._condition;
147 set condition(condition)
149 if (this._condition === condition)
152 this._condition = condition;
154 this.dispatchEventToListeners(WebInspector.Breakpoint.Event.ConditionDidChange);
159 return this._autoContinue;
162 set autoContinue(cont)
164 if (this._autoContinue === cont)
167 this._autoContinue = cont;
169 this.dispatchEventToListeners(WebInspector.Breakpoint.Event.AutoContinueDidChange);
174 return this._actions;
180 condition: this._condition,
181 actions: this._serializableActions(),
182 autoContinue: this._autoContinue
188 // The id, scriptIdentifier and resolved state are tied to the current session, so don't include them for serialization.
191 lineNumber: this._sourceCodeLocation.lineNumber,
192 columnNumber: this._sourceCodeLocation.columnNumber,
193 disabled: this._disabled,
194 condition: this._condition,
195 actions: this._serializableActions(),
196 autoContinue: this._autoContinue
200 appendContextMenuItems: function(contextMenu, breakpointDisplayElement)
202 console.assert(document.body.contains(breakpointDisplayElement), "breakpoint popover display element must be in the DOM");
204 var boundingClientRect = breakpointDisplayElement.getBoundingClientRect();
206 function editBreakpoint()
208 this._showEditBreakpointPopover(boundingClientRect);
211 function removeBreakpoint()
213 WebInspector.debuggerManager.removeBreakpoint(this);
216 function toggleBreakpoint()
218 this.disabled = !this.disabled;
221 function revealOriginalSourceCodeLocation()
223 WebInspector.resourceSidebarPanel.showOriginalOrFormattedSourceCodeLocation(this._sourceCodeLocation);
226 if (WebInspector.debuggerManager.isBreakpointEditable(this))
227 contextMenu.appendItem(WebInspector.UIString("Edit Breakpoint…"), editBreakpoint.bind(this));
230 contextMenu.appendItem(WebInspector.UIString("Enable Breakpoint"), toggleBreakpoint.bind(this));
232 contextMenu.appendItem(WebInspector.UIString("Disable Breakpoint"), toggleBreakpoint.bind(this));
234 if (WebInspector.debuggerManager.isBreakpointRemovable(this)) {
235 contextMenu.appendSeparator();
236 contextMenu.appendItem(WebInspector.UIString("Delete Breakpoint"), removeBreakpoint.bind(this));
239 if (this._sourceCodeLocation.hasMappedLocation()) {
240 contextMenu.appendSeparator();
241 contextMenu.appendItem(WebInspector.UIString("Reveal in Original Resource"), revealOriginalSourceCodeLocation.bind(this));
245 createAction: function(type, precedingAction)
247 var newAction = new WebInspector.BreakpointAction(this, type, null);
249 if (!precedingAction)
250 this._actions.push(newAction);
252 var index = this._actions.indexOf(precedingAction);
253 console.assert(index !== -1);
255 this._actions.push(newAction);
257 this._actions.splice(index + 1, 0, newAction);
260 this.dispatchEventToListeners(WebInspector.Breakpoint.Event.ActionsDidChange);
265 recreateAction: function(type, actionToReplace)
267 var newAction = new WebInspector.BreakpointAction(this, type, null);
269 var index = this._actions.indexOf(actionToReplace);
270 console.assert(index !== -1);
274 this._actions[index] = newAction;
276 this.dispatchEventToListeners(WebInspector.Breakpoint.Event.ActionsDidChange);
281 removeAction: function(action)
283 var index = this._actions.indexOf(action);
284 console.assert(index !== -1);
288 this._actions.splice(index, 1);
290 this.dispatchEventToListeners(WebInspector.Breakpoint.Event.ActionsDidChange);
293 // Protected (Called by BreakpointAction)
295 breakpointActionDidChange: function(action)
297 var index = this._actions.indexOf(action);
298 console.assert(index !== -1);
302 this.dispatchEventToListeners(WebInspector.Breakpoint.Event.ActionsDidChange);
307 _serializableActions: function()
310 for (var i = 0; i < this._actions.length; ++i)
311 actions.push(this._actions[i].info);
315 _popoverToggleEnabledCheckboxChanged: function(event)
317 this.disabled = !event.target.checked;
320 _popoverConditionInputChanged: function(event)
322 this.condition = event.target.value;
325 _popoverToggleAutoContinueCheckboxChanged: function(event)
327 this.autoContinue = event.target.checked;
330 _popoverConditionInputKeyDown: function(event)
332 if (this._keyboardShortcutEsc.matchesEvent(event) || this._keyboardShortcutEnter.matchesEvent(event)) {
333 this._popover.dismiss();
334 event.stopPropagation();
335 event.preventDefault();
339 _editBreakpointPopoverContentElement: function()
341 var content = this._popoverContentElement = document.createElement("div");
342 content.className = WebInspector.Breakpoint.PopoverClassName;
344 var checkboxElement = document.createElement("input");
345 checkboxElement.type = "checkbox";
346 checkboxElement.checked = !this._disabled;
347 checkboxElement.addEventListener("change", this._popoverToggleEnabledCheckboxChanged.bind(this));
349 var checkboxLabel = document.createElement("label");
350 checkboxLabel.className = "toggle";
351 checkboxLabel.appendChild(checkboxElement);
352 checkboxLabel.appendChild(document.createTextNode(this._sourceCodeLocation.displayLocationString()));
354 var table = document.createElement("table");
356 var conditionRow = table.appendChild(document.createElement("tr"));
357 var conditionHeader = conditionRow.appendChild(document.createElement("th"));
358 var conditionData = conditionRow.appendChild(document.createElement("td"));
359 var conditionLabel = conditionHeader.appendChild(document.createElement("label"));
360 var conditionInput = conditionData.appendChild(document.createElement("input"));
361 conditionInput.id = WebInspector.Breakpoint.PopoverConditionInputId;
362 conditionInput.value = this._condition || "";
363 conditionInput.spellcheck = false;
364 conditionInput.addEventListener("change", this._popoverConditionInputChanged.bind(this));
365 conditionInput.addEventListener("keydown", this._popoverConditionInputKeyDown.bind(this));
366 conditionInput.placeholder = WebInspector.UIString("Conditional expression");
367 conditionLabel.setAttribute("for", conditionInput.id);
368 conditionLabel.textContent = WebInspector.UIString("Condition");
370 if (DebuggerAgent.setBreakpoint.supports("options")) {
371 var actionRow = table.appendChild(document.createElement("tr"));
372 var actionHeader = actionRow.appendChild(document.createElement("th"));
373 var actionData = this._actionsContainer = actionRow.appendChild(document.createElement("td"));
374 var actionLabel = actionHeader.appendChild(document.createElement("label"));
375 actionLabel.textContent = WebInspector.UIString("Action");
377 if (!this._actions.length)
378 this._popoverActionsCreateAddActionButton();
380 this._popoverContentElement.classList.add(WebInspector.Breakpoint.WidePopoverClassName);
381 for (var i = 0; i < this._actions.length; ++i) {
382 var breakpointActionView = new WebInspector.BreakpointActionView(this._actions[i], this, true);
383 this._popoverActionsInsertBreakpointActionView(breakpointActionView, i);
387 var optionsRow = table.appendChild(document.createElement("tr"));
388 var optionsHeader = optionsRow.appendChild(document.createElement("th"));
389 var optionsData = optionsRow.appendChild(document.createElement("td"));
390 var optionsLabel = optionsHeader.appendChild(document.createElement("label"));
391 var optionsCheckbox = optionsData.appendChild(document.createElement("input"));
392 var optionsCheckboxLabel = optionsData.appendChild(document.createElement("label"));
393 optionsCheckbox.id = WebInspector.Breakpoint.PopoverOptionsAutoContinueInputId;
394 optionsCheckbox.type = "checkbox";
395 optionsCheckbox.checked = this._autoContinue;
396 optionsCheckbox.addEventListener("change", this._popoverToggleAutoContinueCheckboxChanged.bind(this));
397 optionsLabel.textContent = WebInspector.UIString("Options");
398 optionsCheckboxLabel.setAttribute("for", optionsCheckbox.id);
399 optionsCheckboxLabel.textContent = WebInspector.UIString("Automatically continue after evaluating");
402 content.appendChild(checkboxLabel);
403 content.appendChild(table);
408 _popoverActionsCreateAddActionButton: function()
410 this._popoverContentElement.classList.remove(WebInspector.Breakpoint.WidePopoverClassName);
411 this._actionsContainer.removeChildren();
413 var addActionButton = this._actionsContainer.appendChild(document.createElement("button"));
414 addActionButton.textContent = WebInspector.UIString("Add Action");
415 addActionButton.addEventListener("click", this._popoverActionsAddActionButtonClicked.bind(this));
418 _popoverActionsAddActionButtonClicked: function(event)
420 this._popoverContentElement.classList.add(WebInspector.Breakpoint.WidePopoverClassName);
421 this._actionsContainer.removeChildren();
423 var newAction = this.createAction(WebInspector.Breakpoint.DefaultBreakpointActionType);
424 var newBreakpointActionView = new WebInspector.BreakpointActionView(newAction, this);
425 this._popoverActionsInsertBreakpointActionView(newBreakpointActionView, -1);
427 this._popover.update();
430 _popoverActionsInsertBreakpointActionView: function(breakpointActionView, index)
433 this._actionsContainer.appendChild(breakpointActionView.element)
435 var nextElement = this._actionsContainer.children[index + 1] || null;
436 this._actionsContainer.insertBefore(breakpointActionView.element, nextElement);
440 breakpointActionViewAppendActionView: function(breakpointActionView, newAction)
442 var newBreakpointActionView = new WebInspector.BreakpointActionView(newAction, this);
445 var children = this._actionsContainer.children;
446 for (var i = 0; children.length; ++i) {
447 if (children[i] === breakpointActionView.element) {
453 this._popoverActionsInsertBreakpointActionView(newBreakpointActionView, index);
455 this._popover.update();
458 breakpointActionViewRemoveActionView: function(breakpointActionView)
460 breakpointActionView.element.remove();
462 if (!this._actionsContainer.children.length)
463 this._popoverActionsCreateAddActionButton();
465 this._popover.update();
468 breakpointActionViewResized: function(breakpointActionView)
470 this._popover.update();
473 willDismissPopover: function(popover)
475 console.assert(this._popover === popover);
476 delete this._popoverContentElement;
477 delete this._actionsContainer;
478 delete this._popover;
481 _showEditBreakpointPopover: function(boundingClientRect)
484 var bounds = WebInspector.Rect.rectFromClientRect(boundingClientRect);
486 bounds.origin.x -= 1; // Move the anchor left one pixel so it looks more centered.
487 bounds.origin.x -= padding;
488 bounds.origin.y -= padding;
489 bounds.size.width += padding * 2;
490 bounds.size.height += padding * 2;
492 this._popover = this._popover || new WebInspector.Popover(this);
493 this._popover.content = this._editBreakpointPopoverContentElement();
494 this._popover.present(bounds, [WebInspector.RectEdge.MAX_Y]);
496 if (!this._keyboardShortcutEsc) {
497 this._keyboardShortcutEsc = new WebInspector.KeyboardShortcut(null, WebInspector.KeyboardShortcut.Key.Escape);
498 this._keyboardShortcutEnter = new WebInspector.KeyboardShortcut(null, WebInspector.KeyboardShortcut.Key.Enter);
501 document.getElementById(WebInspector.Breakpoint.PopoverConditionInputId).select();
504 _sourceCodeLocationLocationChanged: function(event)
506 this.dispatchEventToListeners(WebInspector.Breakpoint.Event.LocationDidChange, event.data);
509 _sourceCodeLocationDisplayLocationChanged: function(event)
511 this.dispatchEventToListeners(WebInspector.Breakpoint.Event.DisplayLocationDidChange, event.data);
515 WebInspector.Breakpoint.prototype.__proto__ = WebInspector.Object.prototype;