| // Copyright 2015 The Cobalt Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| // Help functions |
| // |
| // Convert a string with wildcard pattern to RegExp. |
| // It takes backwards compatible into account, e.g. it replaces empty string |
| // with '*' to match the previous behavior. |
| function createRegExpFromPattern(patternToMatch) { |
| if (!patternToMatch) { |
| // Empty string matches everything. |
| patternToMatch = '*'; |
| } else if (patternToMatch.indexOf('*') < 0) { |
| // When wilecard is not used, match any string that contains it. |
| patternToMatch = '*' + patternToMatch + '*'; |
| } |
| regExpToMatch = patternToMatch.replace(/\./g, '\\.').replace(/\*/g, '.*'); |
| return new RegExp('^' + regExpToMatch + '$'); |
| } |
| |
| // Convert a string with space separated wildcard patterns to RegExps. |
| function createRegExpsFromPatterns(spaceSeparatedPatternsToMatch) { |
| let patternsToMatch; |
| if (spaceSeparatedPatternsToMatch) { |
| patternsToMatch = spaceSeparatedPatternsToMatch.split(/(\s+)/); |
| } else { |
| // Matches everything if the string is empty, to match the previous |
| // behavior. |
| patternsToMatch = ['*']; |
| } |
| let regExpsToMatch = []; |
| patternsToMatch.forEach(function(element) { |
| let trimmedElement = element.trim(); |
| if (trimmedElement) { |
| regExpsToMatch.push(createRegExpFromPattern(trimmedElement)); |
| } |
| }); |
| return regExpsToMatch; |
| } |
| |
| // Return whether |string| matches one of the RegExps specified in |regExps|. |
| function matchStringToRegExps(string, regExps) { |
| for (let i = 0; i < regExps.length; i++) { |
| if (string.match(regExps[i])) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| // Console values constructor |
| function ConsoleValues() { |
| // Use a prefix on all WLS keys to ensure uniqueness. |
| this.KEY_PREFIX = 'cobalt.debugConsole.consoleValues'; |
| // Default key used for auto-save, or if the user doesn't specify another. |
| this.DEFAULT_KEY = 'default'; |
| // Reduced space-separated list of CVal prefixes to display at start-up. |
| this.DEFAULT_ACTIVE_SET = |
| 'Cobalt Memory.CPU Memory.MainWebModule Memory.JS Memory.Font ' + |
| 'Count.MainWebModule.ImageCache.Resource ' + |
| 'Count.MainWebModule.DOM.HtmlElement Count.MainWebModule.Layout.Box ' + |
| 'Event.Count.MainWebModule.KeyDown.DOM.HtmlElement.Document.Added ' + |
| 'Event.Count.MainWebModule.KeyDown.Layout.Box.Created ' + |
| 'Event.Count.MainWebModule.KeyDown.Layout.Box.Destroyed ' + |
| 'Event.Duration.MainWebModule.DOM.VideoStartDelay ' + |
| 'Event.Duration.MainWebModule.KeyDown' |
| |
| this.allCVals = window.debugHub.cVal.keys(); |
| // If true, we will always pull our list of CVals from |
| // this.DEFAULT_ACTIVE_SET. |
| this.useDefaultActiveSet = true; |
| |
| // Do a single update to initialize everything. |
| this.update(); |
| } |
| |
| // Console value tree node constructor |
| function ConsoleValueNode() { |
| this.value = null; |
| this.children = null; |
| } |
| |
| // Helper function for addToTree. |
| // Finds a child node with a specified name, or creates it if necessary. |
| ConsoleValues.prototype.findOrCreateNode = function (name, node) { |
| var child = null; |
| if (node.children.hasOwnProperty(name)) { |
| child = node.children[name]; |
| } else { |
| child = new ConsoleValueNode; |
| node.children[name] = child; |
| } |
| return child; |
| } |
| |
| // Adds a single console value to the tree. |
| // Console value names are split into components by dot separator. If a name |
| // has a single component, it is added directly at this level of the tree with |
| // its value, retrieved from Cobalt. If the name has multiple components, a node |
| // is created for the first component, then this function is called recursively |
| // on the remainder of the name, passing in the prefix (prior components) so the |
| // full name can be constructed for evaluation. |
| ConsoleValues.prototype.addToTree = function(cval, tree, prefix) { |
| if (tree.children == null) { |
| tree.children = new Object; |
| } |
| if (prefix == null) { |
| prefix = ''; |
| } |
| var components = cval.split('.'); |
| if (components.length == 1) { |
| var node = this.findOrCreateNode(cval, tree); |
| var fullName = prefix.length > 0 ? prefix + '.' + cval : cval; |
| node.value = window.debugHub.cVal.getPrettyValue(fullName); |
| } else { |
| var newPrefix = components[0]; |
| var suffix = cval.substring(newPrefix.length + 1); |
| node = this.findOrCreateNode(newPrefix, tree); |
| if (prefix.length > 0) { |
| prefix += '.'; |
| } |
| prefix += newPrefix; |
| this.addToTree(suffix, node, prefix); |
| } |
| return tree; |
| } |
| |
| // Creates a tree from the console values. |
| // Each console value name consists of one or more dot-separated components. |
| // The last component of each console value name corresponds to a leaf node in |
| // the tree; other components correspond to branch nodes. |
| // A display string is then created from this tree, where leaf nodes with |
| // a common ancestor are displayed more compactly. |
| // This function assumes names are sorted alphabetically. |
| ConsoleValues.prototype.buildTree = function(cvals) { |
| var root = new ConsoleValueNode; |
| for (var i = 0; i < cvals.length; i++) { |
| this.addToTree(cvals[i], root, ''); |
| } |
| return root; |
| } |
| |
| // Helper function for consoleValueTreeToString. |
| // Creates the formatted string for a single console value node. In the simplest |
| // case, where the node has no children, this will just be the name and value. |
| // If the node has a single child, we call this function recursively to output |
| // that child, with the full name of this node as a prefix. If the node has |
| // multiple children, we enclose those children in braces and recursively call |
| // treeToString on the sub-tree. |
| ConsoleValues.prototype.nodeToString = function(name, node, prefix) { |
| var result = ''; |
| var value = node.value; |
| var children = node.children; |
| var childNames = children != null ? Object.keys(children) : null; |
| var numChildren = childNames != null ? childNames.length : 0; |
| if (prefix.length > 0) { |
| name = prefix + '.' + name; |
| } |
| if (value != null || numChildren > 1) { |
| result += name + ': '; |
| } |
| if (value != null) { |
| result += value; |
| if (numChildren > 0) { |
| result += ', '; |
| } |
| } |
| if (numChildren == 1) { |
| result += this.nodeToString(childNames[0], children[childNames[0]], name); |
| } else if (numChildren > 1) { |
| result += '{'; |
| result += this.treeToString(children, null); |
| result += '}'; |
| } |
| return result; |
| } |
| |
| // Generates a formatted string from the parsed console value tree. |
| // Uses the helper function nodeToString to process each node. |
| // This function may in turn be called recursively by nodeToString |
| // to generate the string for a sub-tree (when a node has multiple children). |
| ConsoleValues.prototype.treeToString = function(cvalTree, prefix) { |
| if (prefix == null) { |
| prefix = ''; |
| } |
| var result = ''; |
| var names = Object.keys(cvalTree); |
| var numNames = names ? names.length : 0; |
| for (var i = 0; i < numNames; i++) { |
| result += this.nodeToString(names[i], cvalTree[names[i]], prefix) |
| if (i < numNames - 1) { |
| result += ', '; |
| } |
| } |
| return result; |
| } |
| |
| // Updates the complete list of registered CVals. |
| // If any active CVals are no longer registered, remove them from the active |
| // set. |
| ConsoleValues.prototype.updateRegistered = function() { |
| if (this.useDefaultActiveSet) { |
| this.setActiveSetToDefault(); |
| } |
| |
| this.allCVals = window.debugHub.cVal.keys(); |
| for (var i = 0; i < this.activeCVals.length; i++) { |
| if (this.allCVals.indexOf(this.activeCVals[i]) < 0) { |
| this.activeCVals.splice(i, 1); |
| } |
| } |
| } |
| |
| // Updates the tree. |
| ConsoleValues.prototype.update = function() { |
| this.updateRegistered(); |
| this.cvalTree = this.buildTree(this.activeCVals); |
| } |
| |
| // Creates a formatted string from the current tree. |
| ConsoleValues.prototype.toString = function() { |
| if (this.cvalTree && this.cvalTree.children) { |
| return this.treeToString(this.cvalTree.children, null); |
| } else { |
| return 'No CVals to display.'; |
| } |
| } |
| |
| // Lists registered cvals that match one of the patterns in |
| // |spaceSeparatedPatternsToMatch|. |
| ConsoleValues.prototype.list = function(spaceSeparatedPatternsToMatch) { |
| var result = ''; |
| var regExps = createRegExpsFromPatterns(spaceSeparatedPatternsToMatch); |
| var count = 0; |
| for (var i = 0; i < this.allCVals.length; i++) { |
| if (matchStringToRegExps(this.allCVals[i], regExps)) { |
| result += this.allCVals[i] + '\n'; |
| ++count; |
| } |
| } |
| result += '\n' + count + ' cvals total'; |
| return result; |
| } |
| |
| // Adds a single CVal to the active set. |
| ConsoleValues.prototype.addSingleActive = function(cval) { |
| if (this.activeCVals.indexOf(cval) < 0) { |
| this.activeCVals.push(cval); |
| return 'Added cval "' + cval + '" to display list.'; |
| } else { |
| return '"' + cval + '" already in display list.'; |
| } |
| } |
| |
| // Removes a single CVal from the active set. |
| ConsoleValues.prototype.removeSingleActive = function(cval) { |
| var idx = this.activeCVals.indexOf(cval); |
| if (idx >= 0) { |
| this.activeCVals.splice(idx, 1); |
| return 'Removed cval "' + cval + '" from display list.'; |
| } else { |
| return '"' + cval + '" not in display list.'; |
| } |
| } |
| |
| // Adds/removes one or more cvals from the active set. |
| // Each console value is matched against the space-separated list of patterns |
| // and the specified method run on each match. |
| ConsoleValues.prototype.updateActiveSet = |
| function(onMatch, spaceSeparatedPatternsToMatch) { |
| var result = ''; |
| var regExps = createRegExpsFromPatterns(spaceSeparatedPatternsToMatch); |
| var processedCvals = []; |
| for (var i = 0; i < regExps.length; ++i) { |
| var found = false; |
| for (var j = 0; j < this.allCVals.length; j++) { |
| if (this.allCVals[j].match(regExps[i])) { |
| found = true; |
| if (processedCvals.indexOf(this.allCVals[j]) === -1) { |
| processedCvals.push(this.allCVals[j]); |
| result += onMatch.call(this, this.allCVals[j]) + '\n'; |
| } |
| } |
| } |
| if (!found) { |
| result += '"' + regExps[i] + '" matched no registered cvals.\n'; |
| } |
| } |
| this.activeCVals.sort(); |
| return result; |
| } |
| |
| |
| // Saves the current active set using the web local storage. |
| ConsoleValues.prototype.saveActiveSet = function(key) { |
| if (this.useDefaultActiveSet) { |
| // If we are using the default CVals and we go to save our CVal set, lock |
| // it in to the currently displayed list. |
| this.updateRegistered(); |
| this.useDefaultActiveSet = false; |
| } |
| |
| if (!key || !key.length) { |
| key = this.DEFAULT_KEY; |
| } |
| var longKey = this.KEY_PREFIX + '.' + key; |
| var value = ''; |
| for (var i = 0; i < this.activeCVals.length; i++) { |
| value += this.activeCVals[i]; |
| if (i < this.activeCVals.length - 1) { |
| value += ' '; |
| } |
| } |
| window.localStorage.setItem(longKey, value); |
| return 'Stored current CVal display set to "' + longKey + '"'; |
| } |
| |
| // Loads an active set from web local storage. |
| ConsoleValues.prototype.loadActiveSet = function(key) { |
| if (!key || !key.length) { |
| key = this.DEFAULT_KEY; |
| } |
| var longKey = this.KEY_PREFIX + '.' + key; |
| var value = window.localStorage.getItem(longKey); |
| if (value && value.length > 0) { |
| // If we load CVals from disk, we no longer use the default set. |
| this.useDefaultActiveSet = false; |
| this.activeCVals = value.split(' '); |
| return 'Loaded CVal display set from "' + longKey + '"'; |
| } else { |
| return 'Could not load CVal display set from "' + longKey + '"'; |
| } |
| } |
| |
| // Sets the CVal active set to the set of CVals the match the default prefixes. |
| // Any of the registered console values that match one of the space-separated |
| ConsoleValues.prototype.setActiveSetToDefault = function() { |
| this.activeCVals = []; |
| this.updateActiveSet(this.addSingleActive, this.DEFAULT_ACTIVE_SET); |
| } |
| |
| // Adds one or more CVals to the active list. |
| // Any of the registered console values that match one of the space-separated |
| // set of patterns will be added to the active set. |
| ConsoleValues.prototype.addActive = function(spaceSeparatedPatternsToMatch) { |
| if (this.useDefaultActiveSet) { |
| // If we modify our list of CVals, we should no longer rely on the defaults. |
| this.updateRegistered(); |
| this.useDefaultActiveSet = false; |
| } |
| return this.updateActiveSet(this.addSingleActive, |
| spaceSeparatedPatternsToMatch); |
| } |
| |
| // Removes one or more CVals from the active list. |
| // Any of the registered console values that match one of the space-separated |
| // set of patterns will be removed from the active set. |
| ConsoleValues.prototype.removeActive = function(spaceSeparatedPatternsToMatch) { |
| if (this.useDefaultActiveSet) { |
| // If we modify our list of CVals, we should no longer rely on the defaults. |
| this.updateRegistered(); |
| this.useDefaultActiveSet = false; |
| } |
| return this.updateActiveSet(this.removeSingleActive, |
| spaceSeparatedPatternsToMatch); |
| } |