1 /* 2 * Copyright (C) 2009 Google 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 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 Google Inc. 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 /** 32 * @constructor 33 */ 34 WebInspector.Settings = function() 35 { 36 this._eventSupport = new WebInspector.Object(); 37 this._registry = /** @type {!Object.<string, !WebInspector.Setting>} */ ({}); 38 39 this.colorFormat = this.createSetting("colorFormat", "original"); 40 this.consoleHistory = this.createSetting("consoleHistory", []); 41 this.domWordWrap = this.createSetting("domWordWrap", true); 42 this.eventListenersFilter = this.createSetting("eventListenersFilter", "all"); 43 this.lastViewedScriptFile = this.createSetting("lastViewedScriptFile", "application"); 44 this.monitoringXHREnabled = this.createSetting("monitoringXHREnabled", false); 45 this.preserveConsoleLog = this.createSetting("preserveConsoleLog", false); 46 this.consoleTimestampsEnabled = this.createSetting("consoleTimestampsEnabled", false); 47 this.resourcesLargeRows = this.createSetting("resourcesLargeRows", true); 48 this.resourcesSortOptions = this.createSetting("resourcesSortOptions", {timeOption: "responseTime", sizeOption: "transferSize"}); 49 this.resourceViewTab = this.createSetting("resourceViewTab", "preview"); 50 this.showInheritedComputedStyleProperties = this.createSetting("showInheritedComputedStyleProperties", false); 51 this.showUserAgentStyles = this.createSetting("showUserAgentStyles", true); 52 this.watchExpressions = this.createSetting("watchExpressions", []); 53 this.breakpoints = this.createSetting("breakpoints", []); 54 this.eventListenerBreakpoints = this.createSetting("eventListenerBreakpoints", []); 55 this.domBreakpoints = this.createSetting("domBreakpoints", []); 56 this.xhrBreakpoints = this.createSetting("xhrBreakpoints", []); 57 this.jsSourceMapsEnabled = this.createSetting("sourceMapsEnabled", true); 58 this.cssSourceMapsEnabled = this.createSetting("cssSourceMapsEnabled", true); 59 this.cacheDisabled = this.createSetting("cacheDisabled", false); 60 this.showUAShadowDOM = this.createSetting("showUAShadowDOM", false); 61 this.savedURLs = this.createSetting("savedURLs", {}); 62 this.javaScriptDisabled = this.createSetting("javaScriptDisabled", false); 63 this.showAdvancedHeapSnapshotProperties = this.createSetting("showAdvancedHeapSnapshotProperties", false); 64 this.recordAllocationStacks = this.createSetting("recordAllocationStacks", false); 65 this.highResolutionCpuProfiling = this.createSetting("highResolutionCpuProfiling", false); 66 this.searchInContentScripts = this.createSetting("searchInContentScripts", false); 67 this.textEditorIndent = this.createSetting("textEditorIndent", " "); 68 this.textEditorAutoDetectIndent = this.createSetting("textEditorAutoIndentIndent", true); 69 this.textEditorAutocompletion = this.createSetting("textEditorAutocompletion", true); 70 this.textEditorBracketMatching = this.createSetting("textEditorBracketMatching", true); 71 this.cssReloadEnabled = this.createSetting("cssReloadEnabled", false); 72 this.timelineLiveUpdate = this.createSetting("timelineLiveUpdate", true); 73 this.showMetricsRulers = this.createSetting("showMetricsRulers", false); 74 this.workerInspectorWidth = this.createSetting("workerInspectorWidth", 600); 75 this.workerInspectorHeight = this.createSetting("workerInspectorHeight", 600); 76 this.messageURLFilters = this.createSetting("messageURLFilters", {}); 77 this.networkHideDataURL = this.createSetting("networkHideDataURL", false); 78 this.networkResourceTypeFilters = this.createSetting("networkResourceTypeFilters", {}); 79 this.messageLevelFilters = this.createSetting("messageLevelFilters", {}); 80 this.splitVerticallyWhenDockedToRight = this.createSetting("splitVerticallyWhenDockedToRight", true); 81 this.visiblePanels = this.createSetting("visiblePanels", {}); 82 this.shortcutPanelSwitch = this.createSetting("shortcutPanelSwitch", false); 83 this.showWhitespacesInEditor = this.createSetting("showWhitespacesInEditor", false); 84 this.skipStackFramesPattern = this.createRegExpSetting("skipStackFramesPattern", ""); 85 this.skipContentScripts = this.createSetting("skipContentScripts", false); 86 this.pauseOnExceptionEnabled = this.createSetting("pauseOnExceptionEnabled", false); 87 this.pauseOnCaughtException = this.createSetting("pauseOnCaughtException", false); 88 this.enableAsyncStackTraces = this.createSetting("enableAsyncStackTraces", false); 89 this.showMediaQueryInspector = this.createSetting("showMediaQueryInspector", false); 90 this.disableOverridesWarning = this.createSetting("disableOverridesWarning", false); 91 92 // Rendering options 93 this.showPaintRects = this.createSetting("showPaintRects", false); 94 this.showDebugBorders = this.createSetting("showDebugBorders", false); 95 this.showFPSCounter = this.createSetting("showFPSCounter", false); 96 this.continuousPainting = this.createSetting("continuousPainting", false); 97 this.showScrollBottleneckRects = this.createSetting("showScrollBottleneckRects", false); 98 } 99 100 WebInspector.Settings.prototype = { 101 /** 102 * @param {string} key 103 * @param {*} defaultValue 104 * @return {!WebInspector.Setting} 105 */ 106 createSetting: function(key, defaultValue) 107 { 108 if (!this._registry[key]) 109 this._registry[key] = new WebInspector.Setting(key, defaultValue, this._eventSupport, window.localStorage); 110 return this._registry[key]; 111 }, 112 113 /** 114 * @param {string} key 115 * @param {string} defaultValue 116 * @param {string=} regexFlags 117 * @return {!WebInspector.Setting} 118 */ 119 createRegExpSetting: function(key, defaultValue, regexFlags) 120 { 121 if (!this._registry[key]) 122 this._registry[key] = new WebInspector.RegExpSetting(key, defaultValue, this._eventSupport, window.localStorage, regexFlags); 123 return this._registry[key]; 124 } 125 } 126 127 /** 128 * @constructor 129 * @param {string} name 130 * @param {V} defaultValue 131 * @param {!WebInspector.Object} eventSupport 132 * @param {?Storage} storage 133 * @template V 134 */ 135 WebInspector.Setting = function(name, defaultValue, eventSupport, storage) 136 { 137 this._name = name; 138 this._defaultValue = defaultValue; 139 this._eventSupport = eventSupport; 140 this._storage = storage; 141 } 142 143 WebInspector.Setting.prototype = { 144 /** 145 * @param {function(!WebInspector.Event)} listener 146 * @param {!Object=} thisObject 147 */ 148 addChangeListener: function(listener, thisObject) 149 { 150 this._eventSupport.addEventListener(this._name, listener, thisObject); 151 }, 152 153 /** 154 * @param {function(!WebInspector.Event)} listener 155 * @param {!Object=} thisObject 156 */ 157 removeChangeListener: function(listener, thisObject) 158 { 159 this._eventSupport.removeEventListener(this._name, listener, thisObject); 160 }, 161 162 get name() 163 { 164 return this._name; 165 }, 166 167 /** 168 * @return {V} 169 */ 170 get: function() 171 { 172 if (typeof this._value !== "undefined") 173 return this._value; 174 175 this._value = this._defaultValue; 176 if (this._storage && this._name in this._storage) { 177 try { 178 this._value = JSON.parse(this._storage[this._name]); 179 } catch(e) { 180 delete this._storage[this._name]; 181 } 182 } 183 return this._value; 184 }, 185 186 /** 187 * @param {V} value 188 */ 189 set: function(value) 190 { 191 this._value = value; 192 if (this._storage) { 193 try { 194 var settingString = JSON.stringify(value); 195 try { 196 this._storage[this._name] = settingString; 197 } catch(e) { 198 this._printSettingsSavingError(e.message, this._name, settingString); 199 } 200 } catch(e) { 201 WebInspector.console.error("Cannot stringify setting with name: " + this._name + ", error: " + e.message); 202 } 203 } 204 this._eventSupport.dispatchEventToListeners(this._name, value); 205 }, 206 207 /** 208 * @param {string} message 209 * @param {string} name 210 * @param {string} value 211 */ 212 _printSettingsSavingError: function(message, name, value) 213 { 214 var errorMessage = "Error saving setting with name: " + this._name + ", value length: " + value.length + ". Error: " + message; 215 console.error(errorMessage); 216 WebInspector.console.error(errorMessage); 217 WebInspector.console.log("Ten largest settings: "); 218 219 var sizes = { __proto__: null }; 220 for (var key in this._storage) 221 sizes[key] = this._storage.getItem(key).length; 222 var keys = Object.keys(sizes); 223 224 function comparator(key1, key2) 225 { 226 return sizes[key2] - sizes[key1]; 227 } 228 229 keys.sort(comparator); 230 231 for (var i = 0; i < 10 && i < keys.length; ++i) 232 WebInspector.console.log("Setting: '" + keys[i] + "', size: " + sizes[keys[i]]); 233 }, 234 } 235 236 /** 237 * @constructor 238 * @extends {WebInspector.Setting} 239 * @param {string} name 240 * @param {string} defaultValue 241 * @param {!WebInspector.Object} eventSupport 242 * @param {?Storage} storage 243 * @param {string=} regexFlags 244 */ 245 WebInspector.RegExpSetting = function(name, defaultValue, eventSupport, storage, regexFlags) 246 { 247 WebInspector.Setting.call(this, name, defaultValue ? [{ pattern: defaultValue }] : [], eventSupport, storage); 248 this._regexFlags = regexFlags; 249 } 250 251 WebInspector.RegExpSetting.prototype = { 252 /** 253 * @override 254 * @return {string} 255 */ 256 get: function() 257 { 258 var result = []; 259 var items = this.getAsArray(); 260 for (var i = 0; i < items.length; ++i) { 261 var item = items[i]; 262 if (item.pattern && !item.disabled) 263 result.push(item.pattern); 264 } 265 return result.join("|"); 266 }, 267 268 /** 269 * @return {!Array.<{pattern: string, disabled: (boolean|undefined)}>} 270 */ 271 getAsArray: function() 272 { 273 return WebInspector.Setting.prototype.get.call(this); 274 }, 275 276 /** 277 * @override 278 * @param {string} value 279 */ 280 set: function(value) 281 { 282 this.setAsArray([{ pattern: value }]); 283 }, 284 285 /** 286 * @param {!Array.<{pattern: string, disabled: (boolean|undefined)}>} value 287 */ 288 setAsArray: function(value) 289 { 290 delete this._regex; 291 WebInspector.Setting.prototype.set.call(this, value); 292 }, 293 294 /** 295 * @return {?RegExp} 296 */ 297 asRegExp: function() 298 { 299 if (typeof this._regex !== "undefined") 300 return this._regex; 301 this._regex = null; 302 try { 303 var pattern = this.get(); 304 if (pattern) 305 this._regex = new RegExp(pattern, this._regexFlags || ""); 306 } catch (e) { 307 } 308 return this._regex; 309 }, 310 311 __proto__: WebInspector.Setting.prototype 312 } 313 314 /** 315 * @constructor 316 */ 317 WebInspector.VersionController = function() 318 { 319 } 320 321 WebInspector.VersionController.currentVersion = 10; 322 323 WebInspector.VersionController.prototype = { 324 updateVersion: function() 325 { 326 var versionSetting = WebInspector.settings.createSetting("inspectorVersion", 0); 327 var currentVersion = WebInspector.VersionController.currentVersion; 328 var oldVersion = versionSetting.get(); 329 var methodsToRun = this._methodsToRunToUpdateVersion(oldVersion, currentVersion); 330 for (var i = 0; i < methodsToRun.length; ++i) 331 this[methodsToRun[i]].call(this); 332 versionSetting.set(currentVersion); 333 }, 334 335 /** 336 * @param {number} oldVersion 337 * @param {number} currentVersion 338 */ 339 _methodsToRunToUpdateVersion: function(oldVersion, currentVersion) 340 { 341 var result = []; 342 for (var i = oldVersion; i < currentVersion; ++i) 343 result.push("_updateVersionFrom" + i + "To" + (i + 1)); 344 return result; 345 }, 346 347 _updateVersionFrom0To1: function() 348 { 349 this._clearBreakpointsWhenTooMany(WebInspector.settings.breakpoints, 500000); 350 }, 351 352 _updateVersionFrom1To2: function() 353 { 354 var versionSetting = WebInspector.settings.createSetting("previouslyViewedFiles", []); 355 versionSetting.set([]); 356 }, 357 358 _updateVersionFrom2To3: function() 359 { 360 var fileSystemMappingSetting = WebInspector.settings.createSetting("fileSystemMapping", {}); 361 fileSystemMappingSetting.set({}); 362 if (window.localStorage) 363 delete window.localStorage["fileMappingEntries"]; 364 }, 365 366 _updateVersionFrom3To4: function() 367 { 368 var advancedMode = WebInspector.settings.createSetting("showHeaSnapshotObjectsHiddenProperties", false).get(); 369 WebInspector.settings.showAdvancedHeapSnapshotProperties.set(advancedMode); 370 }, 371 372 _updateVersionFrom4To5: function() 373 { 374 if (!window.localStorage) 375 return; 376 var settingNames = { 377 "FileSystemViewSidebarWidth": "fileSystemViewSplitViewState", 378 "canvasProfileViewReplaySplitLocation": "canvasProfileViewReplaySplitViewState", 379 "canvasProfileViewSplitLocation": "canvasProfileViewSplitViewState", 380 "elementsSidebarWidth": "elementsPanelSplitViewState", 381 "StylesPaneSplitRatio": "stylesPaneSplitViewState", 382 "heapSnapshotRetainersViewSize": "heapSnapshotSplitViewState", 383 "InspectorView.splitView": "InspectorView.splitViewState", 384 "InspectorView.screencastSplitView": "InspectorView.screencastSplitViewState", 385 "Inspector.drawerSplitView": "Inspector.drawerSplitViewState", 386 "layerDetailsSplitView": "layerDetailsSplitViewState", 387 "networkSidebarWidth": "networkPanelSplitViewState", 388 "sourcesSidebarWidth": "sourcesPanelSplitViewState", 389 "scriptsPanelNavigatorSidebarWidth": "sourcesPanelNavigatorSplitViewState", 390 "sourcesPanelSplitSidebarRatio": "sourcesPanelDebuggerSidebarSplitViewState", 391 "timeline-details": "timelinePanelDetailsSplitViewState", 392 "timeline-split": "timelinePanelRecorsSplitViewState", 393 "timeline-view": "timelinePanelTimelineStackSplitViewState", 394 "auditsSidebarWidth": "auditsPanelSplitViewState", 395 "layersSidebarWidth": "layersPanelSplitViewState", 396 "profilesSidebarWidth": "profilesPanelSplitViewState", 397 "resourcesSidebarWidth": "resourcesPanelSplitViewState" 398 }; 399 for (var oldName in settingNames) { 400 var newName = settingNames[oldName]; 401 var oldNameH = oldName + "H"; 402 403 var newValue = null; 404 var oldSetting = WebInspector.settings.createSetting(oldName, undefined).get(); 405 if (oldSetting) { 406 newValue = newValue || {}; 407 newValue.vertical = {}; 408 newValue.vertical.size = oldSetting; 409 delete window.localStorage[oldName]; 410 } 411 var oldSettingH = WebInspector.settings.createSetting(oldNameH, undefined).get(); 412 if (oldSettingH) { 413 newValue = newValue || {}; 414 newValue.horizontal = {}; 415 newValue.horizontal.size = oldSettingH; 416 delete window.localStorage[oldNameH]; 417 } 418 var newSetting = WebInspector.settings.createSetting(newName, {}); 419 if (newValue) 420 newSetting.set(newValue); 421 } 422 }, 423 424 _updateVersionFrom5To6: function() 425 { 426 if (!window.localStorage) 427 return; 428 429 var settingNames = { 430 "debuggerSidebarHidden": "sourcesPanelSplitViewState", 431 "navigatorHidden": "sourcesPanelNavigatorSplitViewState", 432 "WebInspector.Drawer.showOnLoad": "Inspector.drawerSplitViewState" 433 }; 434 435 for (var oldName in settingNames) { 436 var newName = settingNames[oldName]; 437 438 var oldSetting = WebInspector.settings.createSetting(oldName, undefined).get(); 439 var invert = "WebInspector.Drawer.showOnLoad" === oldName; 440 var hidden = !!oldSetting !== invert; 441 delete window.localStorage[oldName]; 442 var showMode = hidden ? "OnlyMain" : "Both"; 443 444 var newSetting = WebInspector.settings.createSetting(newName, null); 445 var newValue = newSetting.get() || {}; 446 newValue.vertical = newValue.vertical || {}; 447 newValue.vertical.showMode = showMode; 448 newValue.horizontal = newValue.horizontal || {}; 449 newValue.horizontal.showMode = showMode; 450 newSetting.set(newValue); 451 } 452 }, 453 454 _updateVersionFrom6To7: function() 455 { 456 if (!window.localStorage) 457 return; 458 459 var settingNames = { 460 "sourcesPanelNavigatorSplitViewState": "sourcesPanelNavigatorSplitViewState", 461 "elementsPanelSplitViewState": "elementsPanelSplitViewState", 462 "canvasProfileViewReplaySplitViewState": "canvasProfileViewReplaySplitViewState", 463 "stylesPaneSplitViewState": "stylesPaneSplitViewState", 464 "sourcesPanelDebuggerSidebarSplitViewState": "sourcesPanelDebuggerSidebarSplitViewState" 465 }; 466 467 for (var name in settingNames) { 468 if (!(name in window.localStorage)) 469 continue; 470 var setting = WebInspector.settings.createSetting(name, undefined); 471 var value = setting.get(); 472 if (!value) 473 continue; 474 // Zero out saved percentage sizes, and they will be restored to defaults. 475 if (value.vertical && value.vertical.size && value.vertical.size < 1) 476 value.vertical.size = 0; 477 if (value.horizontal && value.horizontal.size && value.horizontal.size < 1) 478 value.horizontal.size = 0; 479 setting.set(value); 480 } 481 }, 482 483 _updateVersionFrom7To8: function() 484 { 485 var settingName = "deviceMetrics"; 486 if (!window.localStorage || !(settingName in window.localStorage)) 487 return; 488 var setting = WebInspector.settings.createSetting(settingName, undefined); 489 var value = setting.get(); 490 if (!value) 491 return; 492 493 var components = value.split("x"); 494 if (components.length >= 3) { 495 var width = parseInt(components[0], 10); 496 var height = parseInt(components[1], 10); 497 var deviceScaleFactor = parseFloat(components[2]); 498 if (deviceScaleFactor) { 499 components[0] = "" + Math.round(width / deviceScaleFactor); 500 components[1] = "" + Math.round(height / deviceScaleFactor); 501 } 502 } 503 value = components.join("x"); 504 setting.set(value); 505 }, 506 507 _updateVersionFrom8To9: function() 508 { 509 if (!window.localStorage) 510 return; 511 512 var settingNames = [ 513 "skipStackFramesPattern", 514 "workspaceFolderExcludePattern" 515 ]; 516 517 for (var i = 0; i < settingNames.length; ++i) { 518 var settingName = settingNames[i]; 519 if (!(settingName in window.localStorage)) 520 continue; 521 try { 522 var value = JSON.parse(window.localStorage[settingName]); 523 if (!value) 524 continue; 525 if (typeof value === "string") 526 value = [value]; 527 for (var j = 0; j < value.length; ++j) { 528 if (typeof value[j] === "string") 529 value[j] = { pattern: value[j] }; 530 } 531 window.localStorage[settingName] = JSON.stringify(value); 532 } catch(e) { 533 } 534 } 535 }, 536 537 _updateVersionFrom9To10: function() 538 { 539 if (!window.localStorage) 540 return; 541 542 for (var key in window.localStorage) { 543 if (key.startsWith("revision-history")) 544 window.localStorage.removeItem(key); 545 } 546 }, 547 548 /** 549 * @param {!WebInspector.Setting} breakpointsSetting 550 * @param {number} maxBreakpointsCount 551 */ 552 _clearBreakpointsWhenTooMany: function(breakpointsSetting, maxBreakpointsCount) 553 { 554 // If there are too many breakpoints in a storage, it is likely due to a recent bug that caused 555 // periodical breakpoints duplication leading to inspector slowness. 556 if (breakpointsSetting.get().length > maxBreakpointsCount) 557 breakpointsSetting.set([]); 558 } 559 } 560 561 /** 562 * @type {!WebInspector.Settings} 563 */ 564 WebInspector.settings; 565 566 // These methods are added for backwards compatibility with Devtools CodeSchool extension. 567 // DO NOT REMOVE 568 569 /** 570 * @constructor 571 */ 572 WebInspector.PauseOnExceptionStateSetting = function() 573 { 574 WebInspector.settings.pauseOnExceptionEnabled.addChangeListener(this._enabledChanged, this); 575 WebInspector.settings.pauseOnCaughtException.addChangeListener(this._pauseOnCaughtChanged, this); 576 this._name = "pauseOnExceptionStateString"; 577 this._eventSupport = new WebInspector.Object(); 578 this._value = this._calculateValue(); 579 } 580 581 WebInspector.PauseOnExceptionStateSetting.prototype = { 582 /** 583 * @param {function(!WebInspector.Event)} listener 584 * @param {!Object=} thisObject 585 */ 586 addChangeListener: function(listener, thisObject) 587 { 588 this._eventSupport.addEventListener(this._name, listener, thisObject); 589 }, 590 591 /** 592 * @param {function(!WebInspector.Event)} listener 593 * @param {!Object=} thisObject 594 */ 595 removeChangeListener: function(listener, thisObject) 596 { 597 this._eventSupport.removeEventListener(this._name, listener, thisObject); 598 }, 599 600 /** 601 * @return {string} 602 */ 603 get: function() 604 { 605 return this._value; 606 }, 607 608 /** 609 * @return {string} 610 */ 611 _calculateValue: function() 612 { 613 if (!WebInspector.settings.pauseOnExceptionEnabled.get()) 614 return "none"; 615 // The correct code here would be 616 // return WebInspector.settings.pauseOnCaughtException.get() ? "all" : "uncaught"; 617 // But the CodeSchool DevTools relies on the fact that we used to enable pausing on ALL extensions by default, so we trick it here. 618 return "all"; 619 }, 620 621 _enabledChanged: function(event) 622 { 623 this._fireChangedIfNeeded(); 624 }, 625 626 _pauseOnCaughtChanged: function(event) 627 { 628 this._fireChangedIfNeeded(); 629 }, 630 631 _fireChangedIfNeeded: function() 632 { 633 var newValue = this._calculateValue(); 634 if (newValue === this._value) 635 return; 636 this._value = newValue; 637 this._eventSupport.dispatchEventToListeners(this._name, this._value); 638 } 639 } 640