Web Inspector: Breakpoint Log action should support template literals
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / BreakpointActionView.js
1 /*
2  * Copyright (C) 2013, 2015 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. 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.
24  */
25
26 WebInspector.BreakpointActionView = class BreakpointActionView extends WebInspector.Object
27 {
28     constructor(action, delegate, omitFocus)
29     {
30         super();
31
32         console.assert(action);
33         console.assert(delegate);
34         console.assert(DebuggerAgent.BreakpointActionType);
35
36         this._action = action;
37         this._delegate = delegate;
38
39         this._element = document.createElement("div");
40         this._element.className = "breakpoint-action-block";
41
42         var header = this._element.appendChild(document.createElement("div"));
43         header.className = "breakpoint-action-block-header";
44
45         var picker = header.appendChild(document.createElement("select"));
46         picker.addEventListener("change", this._pickerChanged.bind(this));
47
48         for (var key in WebInspector.BreakpointAction.Type) {
49             var type = WebInspector.BreakpointAction.Type[key];
50             var option = document.createElement("option");
51             option.textContent = WebInspector.BreakpointActionView.displayStringForType(type);
52             option.selected = this._action.type === type;
53             option.value = type;
54             picker.add(option);
55         }
56
57         let buttonContainerElement = header.appendChild(document.createElement("div"));
58         buttonContainerElement.classList.add("breakpoint-action-button-container");
59
60         let appendActionButton = buttonContainerElement.appendChild(document.createElement("button"));
61         appendActionButton.className = "breakpoint-action-append-button";
62         appendActionButton.addEventListener("click", this._appendActionButtonClicked.bind(this));
63         appendActionButton.title = WebInspector.UIString("Add new breakpoint action after this action");
64
65         let removeActionButton = buttonContainerElement.appendChild(document.createElement("button"));
66         removeActionButton.className = "breakpoint-action-remove-button";
67         removeActionButton.addEventListener("click", this._removeAction.bind(this));
68         removeActionButton.title = WebInspector.UIString("Remove this breakpoint action");
69
70         this._bodyElement = this._element.appendChild(document.createElement("div"));
71         this._bodyElement.className = "breakpoint-action-block-body";
72
73         this._updateBody(omitFocus);
74     }
75
76     // Static
77
78     static displayStringForType(type)
79     {
80         switch (type) {
81         case WebInspector.BreakpointAction.Type.Log:
82             return WebInspector.UIString("Log Message");
83         case WebInspector.BreakpointAction.Type.Evaluate:
84             return WebInspector.UIString("Evaluate JavaScript");
85         case WebInspector.BreakpointAction.Type.Sound:
86             return WebInspector.UIString("Play Sound");
87         case WebInspector.BreakpointAction.Type.Probe:
88             return WebInspector.UIString("Probe Expression");
89         default:
90             console.assert(false);
91             return "";
92         }
93     }
94
95     // Public
96
97     get action()
98     {
99         return this._action;
100     }
101
102     get element()
103     {
104         return this._element;
105     }
106
107     // Private
108
109     _pickerChanged(event)
110     {
111         var newType = event.target.value;
112         this._action = this._action.breakpoint.recreateAction(newType, this._action);
113         this._updateBody();
114         this._delegate.breakpointActionViewResized(this);
115     }
116
117     _appendActionButtonClicked(event)
118     {
119         var newAction = this._action.breakpoint.createAction(this._action.type, this._action);
120         this._delegate.breakpointActionViewAppendActionView(this, newAction);
121     }
122
123     _removeAction()
124     {
125         this._action.breakpoint.removeAction(this._action);
126         this._delegate.breakpointActionViewRemoveActionView(this);
127     }
128
129     _updateBody(omitFocus)
130     {
131         this._bodyElement.removeChildren();
132
133         switch (this._action.type) {
134         case WebInspector.BreakpointAction.Type.Log:
135             this._bodyElement.hidden = false;
136
137             var input = this._bodyElement.appendChild(document.createElement("input"));
138             input.placeholder = WebInspector.UIString("Message");
139             input.addEventListener("change", this._logInputChanged.bind(this));
140             input.value = this._action.data || "";
141             input.spellcheck = false;
142             if (!omitFocus)
143                 setTimeout(function() { input.focus(); }, 0);
144
145             let descriptionElement = this._bodyElement.appendChild(document.createElement("div"));
146             descriptionElement.classList.add("description");
147             descriptionElement.textContent = WebInspector.UIString("${expr} = expression");
148             break;
149
150         case WebInspector.BreakpointAction.Type.Evaluate:
151         case WebInspector.BreakpointAction.Type.Probe:
152             this._bodyElement.hidden = false;
153
154             var editorElement = this._bodyElement.appendChild(document.createElement("div"));
155             editorElement.classList.add("breakpoint-action-eval-editor");
156             editorElement.classList.add(WebInspector.SyntaxHighlightedStyleClassName);
157
158             this._codeMirror = WebInspector.CodeMirrorEditor.create(editorElement, {
159                 lineWrapping: true,
160                 mode: "text/javascript",
161                 indentWithTabs: true,
162                 indentUnit: 4,
163                 matchBrackets: true,
164                 value: this._action.data || "",
165             });
166
167             this._codeMirror.on("viewportChange", this._codeMirrorViewportChanged.bind(this));
168             this._codeMirror.on("blur", this._codeMirrorBlurred.bind(this));
169
170             this._codeMirrorViewport = {from: null, to: null};
171
172             var completionController = new WebInspector.CodeMirrorCompletionController(this._codeMirror);
173             completionController.addExtendedCompletionProvider("javascript", WebInspector.javaScriptRuntimeCompletionProvider);
174
175             // CodeMirror needs a refresh after the popover displays, to layout, otherwise it doesn't appear.
176             setTimeout(() => {
177                 this._codeMirror.refresh();
178                 if (!omitFocus)
179                     this._codeMirror.focus();
180             }, 0);
181
182             break;
183
184         case WebInspector.BreakpointAction.Type.Sound:
185             this._bodyElement.hidden = true;
186             break;
187
188         default:
189             console.assert(false);
190             this._bodyElement.hidden = true;
191             break;
192         }
193     }
194
195     _logInputChanged(event)
196     {
197         this._action.data = event.target.value;
198     }
199
200     _codeMirrorBlurred(event)
201     {
202         // Throw away the expression if it's just whitespace.
203         this._action.data = (this._codeMirror.getValue() || "").trim();
204     }
205
206     _codeMirrorViewportChanged(event, from, to)
207     {
208         if (this._codeMirrorViewport.from === from && this._codeMirrorViewport.to === to)
209             return;
210
211         this._codeMirrorViewport.from = from;
212         this._codeMirrorViewport.to = to;
213         this._delegate.breakpointActionViewResized(this);
214     }
215 };