1 /* 2 * Copyright (C) IBM Corp. 2009 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 are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of IBM Corp. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 WebInspector.WatchExpressionsSidebarPane = function() 32 { 33 WebInspector.SidebarPane.call(this, WebInspector.UIString("Watch Expressions")); 34 WebInspector.settings.addEventListener("loaded", this._settingsLoaded, this); 35 } 36 37 WebInspector.WatchExpressionsSidebarPane.prototype = { 38 _settingsLoaded: function() 39 { 40 this.bodyElement.removeChildren(); 41 42 this.expanded = WebInspector.settings.watchExpressions.length > 0; 43 this.section = new WebInspector.WatchExpressionsSection(); 44 this.bodyElement.appendChild(this.section.element); 45 46 var addElement = document.createElement("button"); 47 addElement.setAttribute("type", "button"); 48 addElement.textContent = WebInspector.UIString("Add"); 49 addElement.addEventListener("click", this.section.addExpression.bind(this.section), false); 50 51 var refreshElement = document.createElement("button"); 52 refreshElement.setAttribute("type", "button"); 53 refreshElement.textContent = WebInspector.UIString("Refresh"); 54 refreshElement.addEventListener("click", this.section.update.bind(this.section), false); 55 56 var centerElement = document.createElement("div"); 57 centerElement.addStyleClass("watch-expressions-buttons-container"); 58 centerElement.appendChild(addElement); 59 centerElement.appendChild(refreshElement); 60 this.bodyElement.appendChild(centerElement); 61 62 this.onexpand = this.refreshExpressions.bind(this); 63 }, 64 65 refreshExpressions: function() 66 { 67 if (this.section) 68 this.section.update(); 69 } 70 } 71 72 WebInspector.WatchExpressionsSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype; 73 74 WebInspector.WatchExpressionsSection = function() 75 { 76 this._watchObjectGroupId = "watch-group"; 77 78 WebInspector.ObjectPropertiesSection.call(this); 79 80 this.watchExpressions = WebInspector.settings.watchExpressions; 81 82 this.headerElement.className = "hidden"; 83 this.editable = true; 84 this.expanded = true; 85 this.propertiesElement.addStyleClass("watch-expressions"); 86 } 87 88 WebInspector.WatchExpressionsSection.NewWatchExpression = "\xA0"; 89 90 WebInspector.WatchExpressionsSection.prototype = { 91 update: function() 92 { 93 function appendResult(expression, watchIndex, result, exception) 94 { 95 if (exception) { 96 // Exception results are not wrappers, but text messages. 97 result = WebInspector.ObjectProxy.wrapPrimitiveValue(result); 98 } else if (result.type === "string") { 99 // Evaluation result is intentionally not abbreviated. However, we'd like to distinguish between null and "null" 100 result.description = "\"" + result.description + "\""; 101 } 102 103 var property = new WebInspector.ObjectPropertyProxy(expression, result); 104 property.watchIndex = watchIndex; 105 property.isException = exception; 106 107 // For newly added, empty expressions, set description to "", 108 // since otherwise you get DOMWindow 109 if (property.name === WebInspector.WatchExpressionsSection.NewWatchExpression) 110 property.value.description = ""; 111 112 // To clarify what's going on here: 113 // In the outer function, we calculate the number of properties 114 // that we're going to be updating, and set that in the 115 // propertyCount variable. 116 // In this function, we test to see when we are processing the 117 // last property, and then call the superclass's updateProperties() 118 // method to get all the properties refreshed at once. 119 properties.push(property); 120 121 if (properties.length == propertyCount) { 122 this.updateProperties(properties, WebInspector.WatchExpressionTreeElement, WebInspector.WatchExpressionsSection.CompareProperties); 123 124 // check to see if we just added a new watch expression, 125 // which will always be the last property 126 if (this._newExpressionAdded) { 127 delete this._newExpressionAdded; 128 129 treeElement = this.findAddedTreeElement(); 130 if (treeElement) 131 treeElement.startEditing(); 132 } 133 } 134 } 135 136 // TODO: pass exact injected script id. 137 InspectorBackend.releaseWrapperObjectGroup(0, this._watchObjectGroupId) 138 var properties = []; 139 140 // Count the properties, so we known when to call this.updateProperties() 141 // in appendResult() 142 var propertyCount = 0; 143 for (var i = 0; i < this.watchExpressions.length; ++i) { 144 if (!this.watchExpressions[i]) 145 continue; 146 ++propertyCount; 147 } 148 149 // Now process all the expressions, since we have the actual count, 150 // which is checked in the appendResult inner function. 151 for (var i = 0; i < this.watchExpressions.length; ++i) { 152 var expression = this.watchExpressions[i]; 153 if (!expression) 154 continue; 155 156 WebInspector.console.evalInInspectedWindow("(" + expression + ")", this._watchObjectGroupId, appendResult.bind(this, expression, i)); 157 } 158 159 // note this is setting the expansion of the tree, not the section; 160 // with no expressions, and expanded tree, we get some extra vertical 161 // white space 162 // FIXME: should change to use header buttons instead of the buttons 163 // at the bottom of the section, then we can add a "No Watch Expressions 164 // element when there are no watch expressions, and this issue should 165 // go away. 166 this.expanded = (propertyCount != 0); 167 }, 168 169 addExpression: function() 170 { 171 this._newExpressionAdded = true; 172 this.watchExpressions.push(WebInspector.WatchExpressionsSection.NewWatchExpression); 173 this.update(); 174 }, 175 176 updateExpression: function(element, value) 177 { 178 this.watchExpressions[element.property.watchIndex] = value; 179 this.saveExpressions(); 180 this.update(); 181 }, 182 183 findAddedTreeElement: function() 184 { 185 var children = this.propertiesTreeOutline.children; 186 for (var i = 0; i < children.length; ++i) 187 if (children[i].property.name === WebInspector.WatchExpressionsSection.NewWatchExpression) 188 return children[i]; 189 }, 190 191 saveExpressions: function() 192 { 193 var toSave = []; 194 for (var i = 0; i < this.watchExpressions.length; i++) 195 if (this.watchExpressions[i]) 196 toSave.push(this.watchExpressions[i]); 197 198 WebInspector.settings.watchExpressions = toSave; 199 return toSave.length; 200 } 201 } 202 203 WebInspector.WatchExpressionsSection.prototype.__proto__ = WebInspector.ObjectPropertiesSection.prototype; 204 205 WebInspector.WatchExpressionsSection.CompareProperties = function(propertyA, propertyB) 206 { 207 if (propertyA.watchIndex == propertyB.watchIndex) 208 return 0; 209 else if (propertyA.watchIndex < propertyB.watchIndex) 210 return -1; 211 else 212 return 1; 213 } 214 215 WebInspector.WatchExpressionTreeElement = function(property) 216 { 217 WebInspector.ObjectPropertyTreeElement.call(this, property); 218 } 219 220 WebInspector.WatchExpressionTreeElement.prototype = { 221 update: function() 222 { 223 WebInspector.ObjectPropertyTreeElement.prototype.update.call(this); 224 225 if (this.property.isException) 226 this.valueElement.addStyleClass("watch-expressions-error-level"); 227 228 var deleteButton = document.createElement("input"); 229 deleteButton.type = "button"; 230 deleteButton.title = WebInspector.UIString("Delete watch expression."); 231 deleteButton.addStyleClass("enabled-button"); 232 deleteButton.addStyleClass("delete-button"); 233 deleteButton.addEventListener("click", this._deleteButtonClicked.bind(this), false); 234 235 this.listItemElement.insertBefore(deleteButton, this.listItemElement.firstChild); 236 }, 237 238 _deleteButtonClicked: function() 239 { 240 this.treeOutline.section.updateExpression(this, null); 241 }, 242 243 startEditing: function() 244 { 245 if (WebInspector.isBeingEdited(this.nameElement) || !this.treeOutline.section.editable) 246 return; 247 248 this.nameElement.textContent = this.property.name.trim(); 249 250 var context = { expanded: this.expanded }; 251 252 // collapse temporarily, if required 253 this.hasChildren = false; 254 255 this.listItemElement.addStyleClass("editing-sub-part"); 256 257 WebInspector.startEditing(this.nameElement, this.editingCommitted.bind(this), this.editingCancelled.bind(this), context); 258 }, 259 260 editingCancelled: function(element, context) 261 { 262 if (!this.nameElement.textContent) 263 this.treeOutline.section.updateExpression(this, null); 264 265 this.update(); 266 this.editingEnded(context); 267 }, 268 269 applyExpression: function(expression, updateInterface) 270 { 271 expression = expression.trim(); 272 273 if (!expression) 274 expression = null; 275 276 this.property.name = expression; 277 this.treeOutline.section.updateExpression(this, expression); 278 } 279 } 280 281 WebInspector.WatchExpressionTreeElement.prototype.__proto__ = WebInspector.ObjectPropertyTreeElement.prototype; 282