diff options
author | Feng Qian <fqian@google.com> | 2009-06-17 12:12:20 -0700 |
---|---|---|
committer | Feng Qian <fqian@google.com> | 2009-06-17 12:12:20 -0700 |
commit | 5f1ab04193ad0130ca8204aadaceae083aca9881 (patch) | |
tree | 5a92cd389e2cfe7fb67197ce14b38469462379f8 /WebCore/inspector/front-end | |
parent | 194315e5a908cc8ed67d597010544803eef1ac59 (diff) | |
download | external_webkit-5f1ab04193ad0130ca8204aadaceae083aca9881.zip external_webkit-5f1ab04193ad0130ca8204aadaceae083aca9881.tar.gz external_webkit-5f1ab04193ad0130ca8204aadaceae083aca9881.tar.bz2 |
Get WebKit r44544.
Diffstat (limited to 'WebCore/inspector/front-end')
20 files changed, 1349 insertions, 323 deletions
diff --git a/WebCore/inspector/front-end/BottomUpProfileDataGridTree.js b/WebCore/inspector/front-end/BottomUpProfileDataGridTree.js new file mode 100644 index 0000000..89b4ddc --- /dev/null +++ b/WebCore/inspector/front-end/BottomUpProfileDataGridTree.js @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2009 280 North Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Bottom Up Profiling shows the entire callstack backwards: +// The root node is a representation of each individual function called, and each child of that node represents +// a reverse-callstack showing how many of those calls came from it. So, unlike top-down, the statistics in +// each child still represent the root node. We have to be particularly careful of recursion with this mode +// because a root node can represent itself AND an ancestor. + +WebInspector.BottomUpProfileDataGridTree = function(/*ProfileView*/ aProfileView, /*ProfileNode*/ aProfileNode) +{ + WebInspector.ProfileDataGridTree.call(this, aProfileView, aProfileNode); + + // Iterate each node in pre-order. + var profileNodeUIDs = 0; + var profileNodeGroups = [[], [aProfileNode]]; + var visitedProfileNodesForCallUID = {}; + + this._remainingNodeInfos = []; + + for (var profileNodeGroupIndex = 0; profileNodeGroupIndex < profileNodeGroups.length; ++profileNodeGroupIndex) { + var parentProfileNodes = profileNodeGroups[profileNodeGroupIndex]; + var profileNodes = profileNodeGroups[++profileNodeGroupIndex]; + var count = profileNodes.length; + + for (var index = 0; index < count; ++index) { + var profileNode = profileNodes[index]; + + if (!profileNode.UID) + profileNode.UID = ++profileNodeUIDs; + + if (profileNode.head && profileNode !== profileNode.head) { + // The total time of this ancestor is accounted for if we're in any form of recursive cycle. + var visitedNodes = visitedProfileNodesForCallUID[profileNode.callUID]; + var totalTimeAccountedFor = false; + + if (!visitedNodes) { + visitedNodes = {} + visitedProfileNodesForCallUID[profileNode.callUID] = visitedNodes; + } else { + // The total time for this node has already been accounted for iff one of it's parents has already been visited. + // We can do this check in this style because we are traversing the tree in pre-order. + var parentCount = parentProfileNodes.length; + for (var parentIndex = 0; parentIndex < parentCount; ++parentIndex) { + if (visitedNodes[parentProfileNodes[parentIndex].UID]) { + totalTimeAccountedFor = true; + break; + } + } + } + + visitedNodes[profileNode.UID] = true; + + this._remainingNodeInfos.push({ ancestor:profileNode, focusNode:profileNode, totalTimeAccountedFor:totalTimeAccountedFor }); + } + + var children = profileNode.children; + if (children.length) { + profileNodeGroups.push(parentProfileNodes.concat([profileNode])) + profileNodeGroups.push(children); + } + } + } + + // Populate the top level nodes. + WebInspector.BottomUpProfileDataGridNode.prototype._populate.call(this); + + return this; +} + +WebInspector.BottomUpProfileDataGridTree.prototype = { + // When focusing, we keep the entire callstack up to this ancestor. + focus: function(/*ProfileDataGridNode*/ profileDataGridNode) + { + if (!profileDataGridNode) + return; + + this._save(); + + var currentNode = profileDataGridNode; + var focusNode = profileDataGridNode; + + while (currentNode.parent && (currentNode instanceof WebInspector.ProfileDataGridNode)) { + currentNode._takePropertiesFromProfileDataGridNode(profileDataGridNode); + + focusNode = currentNode; + currentNode = currentNode.parent; + + if (currentNode instanceof WebInspector.ProfileDataGridNode) + currentNode._keepOnlyChild(focusNode); + } + + this.children = [focusNode]; + this.totalTime = profileDataGridNode.totalTime; + }, + + exclude: function(/*ProfileDataGridNode*/ profileDataGridNode) + { + if (!profileDataGridNode) + return; + + this._save(); + + var excludedCallUID = profileDataGridNode.callUID; + var excludedTopLevelChild = this.childrenByCallUID[excludedCallUID]; + + // If we have a top level node that is excluded, get rid of it completely (not keeping children), + // since bottom up data relies entirely on the root node. + if (excludedTopLevelChild) + this.children.remove(excludedTopLevelChild); + + var children = this.children; + var count = children.length; + + for (var index = 0; index < count; ++index) + children[index]._exclude(excludedCallUID); + + if (this.lastComparator) + this.sort(this.lastComparator, true); + } +} + +WebInspector.BottomUpProfileDataGridTree.prototype.__proto__ = WebInspector.ProfileDataGridTree.prototype; + +WebInspector.BottomUpProfileDataGridNode = function(/*ProfileView*/ profileView, /*ProfileNode*/ profileNode, /*BottomUpProfileDataGridTree*/ owningTree) +{ + // In bottom up mode, our parents are our children since we display an inverted tree. + // However, we don't want to show the very top parent since it is redundant. + var hasChildren = !!(profileNode.parent && profileNode.parent.parent); + + WebInspector.ProfileDataGridNode.call(this, profileView, profileNode, owningTree, hasChildren); + + this._remainingNodeInfos = []; +} + +WebInspector.BottomUpProfileDataGridNode.prototype = { + _takePropertiesFromProfileDataGridNode: function(/*ProfileDataGridNode*/ profileDataGridNode) + { + this._save(); + + this.selfTime = profileDataGridNode.selfTime; + this.totalTime = profileDataGridNode.totalTime; + this.numberOfCalls = profileDataGridNode.numberOfCalls; + }, + + // When focusing, we keep just the members of the callstack. + _keepOnlyChild: function(/*ProfileDataGridNode*/ child) + { + this._save(); + + this.removeChildren(); + this.appendChild(child); + }, + + _exclude: function(aCallUID) + { + if (this._remainingNodeInfos) + this._populate(); + + this._save(); + + var children = this.children; + var index = this.children.length; + + while (index--) + children[index]._exclude(aCallUID); + + var child = this.childrenByCallUID[aCallUID]; + + if (child) + this._merge(child, true); + }, + + _merge: function(/*ProfileDataGridNode*/ child, /*Boolean*/ shouldAbsorb) + { + this.selfTime -= child.selfTime; + + WebInspector.ProfileDataGridNode.prototype._merge.call(this, child, shouldAbsorb); + }, + + _populate: function(event) + { + var remainingNodeInfos = this._remainingNodeInfos; + var count = remainingNodeInfos.length; + + for (var index = 0; index < count; ++index) { + var nodeInfo = remainingNodeInfos[index]; + var ancestor = nodeInfo.ancestor; + var focusNode = nodeInfo.focusNode; + var child = this.findChild(ancestor); + + // If we already have this child, then merge the data together. + if (child) { + var totalTimeAccountedFor = nodeInfo.totalTimeAccountedFor; + + child.selfTime += focusNode.selfTime; + child.numberOfCalls += focusNode.numberOfCalls; + + if (!totalTimeAccountedFor) + child.totalTime += focusNode.totalTime; + } else { + // If not, add it as a true ancestor. + // In heavy mode, we take our visual identity from ancestor node... + var child = new WebInspector.BottomUpProfileDataGridNode(this.profileView, ancestor, this.tree); + + if (ancestor !== focusNode) { + // but the actual statistics from the "root" node (bottom of the callstack). + child.selfTime = focusNode.selfTime; + child.totalTime = focusNode.totalTime; + child.numberOfCalls = focusNode.numberOfCalls; + } + + this.appendChild(child); + } + + var parent = ancestor.parent; + if (parent && parent.parent) { + nodeInfo.ancestor = parent; + child._remainingNodeInfos.push(nodeInfo); + } + } + + delete this._remainingNodeInfos; + + if (this.removeEventListener) + this.removeEventListener("populate", this._populate, this); + } +} + +WebInspector.BottomUpProfileDataGridNode.prototype.__proto__ = WebInspector.ProfileDataGridNode.prototype; diff --git a/WebCore/inspector/front-end/Console.js b/WebCore/inspector/front-end/Console.js index ba879a0..65cc7d0 100644 --- a/WebCore/inspector/front-end/Console.js +++ b/WebCore/inspector/front-end/Console.js @@ -577,31 +577,36 @@ WebInspector.ConsoleMessage = function(source, level, line, url, groupLevel, rep this.url = url; this.groupLevel = groupLevel; this.repeatCount = repeatCount; - - switch (this.level) { - case WebInspector.ConsoleMessage.MessageLevel.Trace: - var span = document.createElement("span"); - span.addStyleClass("console-formatted-trace"); - var stack = Array.prototype.slice.call(arguments, 6); - var funcNames = stack.map(function(f) { - return f || WebInspector.UIString("(anonymous function)"); - }); - span.appendChild(document.createTextNode(funcNames.join("\n"))); - this.formattedMessage = span; - break; - case WebInspector.ConsoleMessage.MessageLevel.Object: - this.formattedMessage = this._format(["%O", arguments[6]]); - break; - default: - this.formattedMessage = this._format(Array.prototype.slice.call(arguments, 6)); - break; - } - - // This is used for inline message bubbles in SourceFrames, or other plain-text representations. - this.message = this.formattedMessage.textContent; + if (arguments.length > 6) + this.setMessageBody(Array.prototype.slice.call(arguments, 6)); } WebInspector.ConsoleMessage.prototype = { + setMessageBody: function(args) + { + switch (this.level) { + case WebInspector.ConsoleMessage.MessageLevel.Trace: + var span = document.createElement("span"); + span.addStyleClass("console-formatted-trace"); + var stack = Array.prototype.slice.call(args); + var funcNames = stack.map(function(f) { + return f || WebInspector.UIString("(anonymous function)"); + }); + span.appendChild(document.createTextNode(funcNames.join("\n"))); + this.formattedMessage = span; + break; + case WebInspector.ConsoleMessage.MessageLevel.Object: + this.formattedMessage = this._format(["%O", args[0]]); + break; + default: + this.formattedMessage = this._format(args); + break; + } + + // This is used for inline message bubbles in SourceFrames, or other plain-text representations. + this.message = this.formattedMessage.textContent; + }, + isErrorOrWarning: function() { return (this.level === WebInspector.ConsoleMessage.MessageLevel.Warning || this.level === WebInspector.ConsoleMessage.MessageLevel.Error); diff --git a/WebCore/inspector/front-end/DataGrid.js b/WebCore/inspector/front-end/DataGrid.js index 6f103ed..2fcb08c 100644 --- a/WebCore/inspector/front-end/DataGrid.js +++ b/WebCore/inspector/front-end/DataGrid.js @@ -169,32 +169,19 @@ WebInspector.DataGrid.prototype = { insertChild: function(child, index) { if (!child) - throw("Node can't be undefined or null."); + throw("insertChild: Node can't be undefined or null."); if (child.parent === this) - throw("Node is already a child of this node."); + throw("insertChild: Node is already a child of this node."); if (child.parent) child.parent.removeChild(child); - var previousChild = (index > 0 ? this.children[index - 1] : null); - if (previousChild) { - previousChild.nextSibling = child; - child.previousSibling = previousChild; - } else - child.previousSibling = null; - - var nextChild = this.children[index]; - if (nextChild) { - nextChild.previousSibling = child; - child.nextSibling = nextChild; - } else - child.nextSibling = null; - this.children.splice(index, 0, child); this.hasChildren = true; child.parent = this; child.dataGrid = this.dataGrid; + child._recalculateSiblings(index); delete child._depth; delete child._revealed; @@ -216,9 +203,9 @@ WebInspector.DataGrid.prototype = { removeChild: function(child) { if (!child) - throw("Node can't be undefined or null."); + throw("removeChild: Node can't be undefined or null."); if (child.parent !== this) - throw("Node is not a child of this node."); + throw("removeChild: Node is not a child of this node."); child.deselect(); @@ -233,6 +220,9 @@ WebInspector.DataGrid.prototype = { child.parent = null; child.nextSibling = null; child.previousSibling = null; + + if (this.children.length <= 0) + this.hasChildren = false; }, removeChildren: function() @@ -249,6 +239,7 @@ WebInspector.DataGrid.prototype = { } this.children = []; + this.hasChildren = false; }, removeChildrenRecursive: function() @@ -405,7 +396,7 @@ WebInspector.DataGrid.prototype = { var gridNode = this.dataGridNodeFromEvent(event); if (!gridNode || !gridNode.selectable) return; - + if (gridNode.isEventWithinDisclosureTriangle(event)) return; @@ -519,6 +510,34 @@ WebInspector.DataGridNode.prototype = { return true; }, + set hasChildren(x) + { + if (this._hasChildren === x) + return; + + this._hasChildren = x; + + if (!this._element) + return; + + if (this._hasChildren) + { + this._element.addStyleClass("parent"); + if (this.expanded) + this._element.addStyleClass("expanded"); + } + else + { + this._element.removeStyleClass("parent"); + this._element.removeStyleClass("expanded"); + } + }, + + get hasChildren() + { + return this._hasChildren; + }, + set revealed(x) { if (this._revealed === x) @@ -624,6 +643,28 @@ WebInspector.DataGridNode.prototype = { removeChildren: WebInspector.DataGrid.prototype.removeChildren, removeChildrenRecursive: WebInspector.DataGrid.prototype.removeChildrenRecursive, + _recalculateSiblings: function(myIndex) + { + if (!this.parent) + return; + + var previousChild = (myIndex > 0 ? this.parent.children[myIndex - 1] : null); + + if (previousChild) { + previousChild.nextSibling = this; + this.previousSibling = previousChild; + } else + this.previousSibling = null; + + var nextChild = this.parent.children[myIndex + 1]; + + if (nextChild) { + nextChild.previousSibling = this; + this.nextSibling = nextChild; + } else + this.nextSibling = null; + }, + collapse: function() { if (this._element) diff --git a/WebCore/inspector/front-end/ElementsTreeOutline.js b/WebCore/inspector/front-end/ElementsTreeOutline.js index 16e31b8..2da2f10 100644 --- a/WebCore/inspector/front-end/ElementsTreeOutline.js +++ b/WebCore/inspector/front-end/ElementsTreeOutline.js @@ -487,7 +487,7 @@ WebInspector.ElementsTreeElement.prototype = { return; if (this.treeOutline.panel) { - this.treeOutline.rootDOMNode = this.parent.representedObject; + this.treeOutline.rootDOMNode = this.representedObject.parentNode; this.treeOutline.focusedDOMNode = this.representedObject; } @@ -571,7 +571,7 @@ WebInspector.ElementsTreeElement.prototype = { parseContainerElement.innerHTML = "<span " + newText + "></span>"; var parseElement = parseContainerElement.firstChild; if (!parseElement || !parseElement.hasAttributes()) { - editingCancelled(element, context); + this._editingCancelled(element, context); return; } diff --git a/WebCore/inspector/front-end/Images/radioDot.png b/WebCore/inspector/front-end/Images/radioDot.png Binary files differnew file mode 100644 index 0000000..609878f --- /dev/null +++ b/WebCore/inspector/front-end/Images/radioDot.png diff --git a/WebCore/inspector/front-end/PanelEnablerView.js b/WebCore/inspector/front-end/PanelEnablerView.js index 6ec565b..fab6d76 100644 --- a/WebCore/inspector/front-end/PanelEnablerView.js +++ b/WebCore/inspector/front-end/PanelEnablerView.js @@ -44,6 +44,23 @@ WebInspector.PanelEnablerView = function(identifier, headingText, disclaimerText this.headerElement.textContent = headingText; this.choicesForm.appendChild(this.headerElement); + var self = this; + function enableOption(text, checked) { + var label = document.createElement("label"); + var option = document.createElement("input"); + option.type = "radio"; + option.name = "enable-option"; + if (checked) + option.checked = true; + label.appendChild(option); + label.appendChild(document.createTextNode(text)); + self.choicesForm.appendChild(label); + return option; + }; + + this.enabledForSession = enableOption(WebInspector.UIString("Only enable for this session"), true); + this.enabledAlways = enableOption(WebInspector.UIString("Always enable")); + this.disclaimerElement = document.createElement("div"); this.disclaimerElement.className = "panel-enabler-disclaimer"; this.disclaimerElement.textContent = disclaimerText; @@ -70,6 +87,10 @@ WebInspector.PanelEnablerView.prototype = { if (this.element.offsetWidth < (this.choicesForm.offsetWidth + this.imageElement.offsetWidth)) this.imageElement.addStyleClass("hidden"); + }, + + get alwaysEnabled() { + return this.enabledAlways.checked; } } diff --git a/WebCore/inspector/front-end/ProfileDataGridTree.js b/WebCore/inspector/front-end/ProfileDataGridTree.js new file mode 100644 index 0000000..84d9923 --- /dev/null +++ b/WebCore/inspector/front-end/ProfileDataGridTree.js @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2009 280 North Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +WebInspector.ProfileDataGridNode = function(profileView, profileNode, owningTree, hasChildren) +{ + this.profileView = profileView; + this.profileNode = profileNode; + + WebInspector.DataGridNode.call(this, null, hasChildren); + + this.addEventListener("populate", this._populate, this); + + this.tree = owningTree; + + this.childrenByCallUID = {}; + this.lastComparator = null; + + this.callUID = profileNode.callUID; + this.selfTime = profileNode.selfTime; + this.totalTime = profileNode.totalTime; + this.functionName = profileNode.functionName; + this.numberOfCalls = profileNode.numberOfCalls; + this.url = profileNode.url; +} + +WebInspector.ProfileDataGridNode.prototype = { + get data() + { + function formatMilliseconds(time) + { + return Number.secondsToString(time / 1000, WebInspector.UIString.bind(WebInspector), true); + } + + var data = {}; + + data["function"] = this.functionName; + data["calls"] = this.numberOfCalls; + + if (this.profileView.showSelfTimeAsPercent) + data["self"] = WebInspector.UIString("%.2f%%", this.selfPercent); + else + data["self"] = formatMilliseconds(this.selfTime); + + if (this.profileView.showTotalTimeAsPercent) + data["total"] = WebInspector.UIString("%.2f%%", this.totalPercent); + else + data["total"] = formatMilliseconds(this.totalTime); + + if (this.profileView.showAverageTimeAsPercent) + data["average"] = WebInspector.UIString("%.2f%%", this.averagePercent); + else + data["average"] = formatMilliseconds(this.averageTime); + + return data; + }, + + createCell: function(columnIdentifier) + { + var cell = WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier); + + if (columnIdentifier === "self" && this._searchMatchedSelfColumn) + cell.addStyleClass("highlight"); + else if (columnIdentifier === "total" && this._searchMatchedTotalColumn) + cell.addStyleClass("highlight"); + else if (columnIdentifier === "average" && this._searchMatchedAverageColumn) + cell.addStyleClass("highlight"); + else if (columnIdentifier === "calls" && this._searchMatchedCallsColumn) + cell.addStyleClass("highlight"); + + if (columnIdentifier !== "function") + return cell; + + if (this.profileNode._searchMatchedFunctionColumn) + cell.addStyleClass("highlight"); + + if (this.profileNode.url) { + var fileName = WebInspector.displayNameForURL(this.profileNode.url); + + var urlElement = document.createElement("a"); + urlElement.className = "profile-node-file webkit-html-resource-link"; + urlElement.href = this.profileNode.url; + urlElement.lineNumber = this.profileNode.lineNumber; + + if (this.profileNode.lineNumber > 0) + urlElement.textContent = fileName + ":" + this.profileNode.lineNumber; + else + urlElement.textContent = fileName; + + cell.insertBefore(urlElement, cell.firstChild); + } + + return cell; + }, + + select: function(supressSelectedEvent) + { + WebInspector.DataGridNode.prototype.select.call(this, supressSelectedEvent); + this.profileView._dataGridNodeSelected(this); + }, + + deselect: function(supressDeselectedEvent) + { + WebInspector.DataGridNode.prototype.deselect.call(this, supressDeselectedEvent); + this.profileView._dataGridNodeDeselected(this); + }, + + expand: function() + { + if (!this.parent) { + var currentComparator = this.parent.lastComparator; + + if (!currentComparator || (currentComparator === this.lastComparator)) + return; + + this.sort(currentComparator); + } + + WebInspector.DataGridNode.prototype.expand.call(this); + }, + + sort: function(/*Function*/ comparator, /*Boolean*/ force) + { + var gridNodeGroups = [[this]]; + + for (var gridNodeGroupIndex = 0; gridNodeGroupIndex < gridNodeGroups.length; ++gridNodeGroupIndex) { + var gridNodes = gridNodeGroups[gridNodeGroupIndex]; + var count = gridNodes.length; + + for (var index = 0; index < count; ++index) { + var gridNode = gridNodes[index]; + + // If the grid node is collapsed, then don't sort children (save operation for later). + // If the grid node has the same sorting as previously, then there is no point in sorting it again. + if (!force && !gridNode.expanded || gridNode.lastComparator === comparator) + continue; + + gridNode.lastComparator = comparator; + + var children = gridNode.children; + var childCount = children.length; + + if (childCount) { + children.sort(comparator); + + for (var childIndex = 0; childIndex < childCount; ++childIndex) + children[childIndex]._recalculateSiblings(childIndex); + + gridNodeGroups.push(children); + } + } + } + }, + + insertChild: function(/*ProfileDataGridNode*/ profileDataGridNode, index) + { + WebInspector.DataGridNode.prototype.insertChild.call(this, profileDataGridNode, index); + + this.childrenByCallUID[profileDataGridNode.callUID] = profileDataGridNode; + }, + + removeChild: function(/*ProfileDataGridNode*/ profileDataGridNode) + { + WebInspector.DataGridNode.prototype.removeChild.call(this, profileDataGridNode); + + delete this.childrenByCallUID[profileDataGridNode.callUID]; + }, + + removeChildren: function(/*ProfileDataGridNode*/ profileDataGridNode) + { + WebInspector.DataGridNode.prototype.removeChildren.call(this); + + this.childrenByCallUID = {}; + }, + + findChild: function(/*Node*/ node) + { + if (!node) + return null; + return this.childrenByCallUID[node.callUID]; + }, + + get averageTime() + { + return this.selfTime / Math.max(1, this.numberOfCalls); + }, + + get averagePercent() + { + return this.averageTime / this.tree.totalTime * 100.0; + }, + + get selfPercent() + { + return this.selfTime / this.tree.totalTime * 100.0; + }, + + get totalPercent() + { + return this.totalTime / this.tree.totalTime * 100.0; + }, + + // When focusing and collapsing we modify lots of nodes in the tree. + // This allows us to restore them all to their original state when we revert. + _save: function() + { + if (this._savedChildren) + return; + + this._savedSelfTime = this.selfTime; + this._savedTotalTime = this.totalTime; + this._savedNumberOfCalls = this.numberOfCalls; + + this._savedChildren = this.children.slice(); + }, + + // When focusing and collapsing we modify lots of nodes in the tree. + // This allows us to restore them all to their original state when we revert. + _restore: function() + { + if (!this._savedChildren) + return; + + this.selfTime = this._savedSelfTime; + this.totalTime = this._savedTotalTime; + this.numberOfCalls = this._savedNumberOfCalls; + + this.removeChildren(); + + var children = this._savedChildren; + var count = children.length; + + for (var index = 0; index < count; ++index) { + children[index]._restore(); + this.appendChild(children[index]); + } + }, + + _merge: function(child, shouldAbsorb) + { + this.selfTime += child.selfTime; + + if (!shouldAbsorb) { + this.totalTime += child.totalTime; + this.numberOfCalls += child.numberOfCalls; + } + + var children = this.children.slice(); + + this.removeChildren(); + + var count = children.length; + + for (var index = 0; index < count; ++index) { + if (!shouldAbsorb || children[index] !== child) + this.appendChild(children[index]); + } + + children = child.children.slice(); + count = children.length; + + for (var index = 0; index < count; ++index) { + var orphanedChild = children[index], + existingChild = this.childrenByCallUID[orphanedChild.callUID]; + + if (existingChild) + existingChild._merge(orphanedChild, false); + else + this.appendChild(orphanedChild); + } + } +} + +WebInspector.ProfileDataGridNode.prototype.__proto__ = WebInspector.DataGridNode.prototype; + +WebInspector.ProfileDataGridTree = function(profileView, profileNode) +{ + this.tree = this; + this.children = []; + + this.profileView = profileView; + + this.totalTime = profileNode.totalTime; + this.lastComparator = null; + + this.childrenByCallUID = {}; +} + +WebInspector.ProfileDataGridTree.prototype = { + get expanded() + { + return true; + }, + + appendChild: function(child) + { + this.insertChild(child, this.children.length); + }, + + insertChild: function(child, index) + { + this.children.splice(index, 0, child); + this.childrenByCallUID[child.callUID] = child; + }, + + removeChildren: function() + { + this.children = []; + this.childrenByCallUID = {}; + }, + + findChild: WebInspector.ProfileDataGridNode.prototype.findChild, + sort: WebInspector.ProfileDataGridNode.prototype.sort, + + _save: function() + { + if (this._savedChildren) + return; + + this._savedTotalTime = this.totalTime; + this._savedChildren = this.children.slice(); + }, + + restore: function() + { + if (!this._savedChildren) + return; + + this.children = this._savedChildren; + this.totalTime = this._savedTotalTime; + + var children = this.children; + var count = children.length; + + for (var index = 0; index < count; ++index) + children[index]._restore(); + + this._savedChildren = null; + } +} + +WebInspector.ProfileDataGridTree.propertyComparators = [{}, {}]; + +WebInspector.ProfileDataGridTree.propertyComparator = function(/*String*/ property, /*Boolean*/ isAscending) +{ + var comparator = this.propertyComparators[(isAscending ? 1 : 0)][property]; + + if (!comparator) { + if (isAscending) { + comparator = function(lhs, rhs) + { + if (lhs[property] < rhs[property]) + return -1; + + if (lhs[property] > rhs[property]) + return 1; + + return 0; + } + } else { + comparator = function(lhs, rhs) + { + if (lhs[property] > rhs[property]) + return -1; + + if (lhs[property] < rhs[property]) + return 1; + + return 0; + } + } + + this.propertyComparators[(isAscending ? 1 : 0)][property] = comparator; + } + + return comparator; +} diff --git a/WebCore/inspector/front-end/ProfileView.js b/WebCore/inspector/front-end/ProfileView.js index 92e9726..d00733c 100644 --- a/WebCore/inspector/front-end/ProfileView.js +++ b/WebCore/inspector/front-end/ProfileView.js @@ -31,9 +31,11 @@ WebInspector.ProfileView = function(profile) this.showSelfTimeAsPercent = true; this.showTotalTimeAsPercent = true; + this.showAverageTimeAsPercent = true; var columns = { "self": { title: WebInspector.UIString("Self"), width: "72px", sort: "descending", sortable: true }, "total": { title: WebInspector.UIString("Total"), width: "72px", sortable: true }, + "average": { title: WebInspector.UIString("Average"), width: "72px", sortable: true }, "calls": { title: WebInspector.UIString("Calls"), width: "54px", sortable: true }, "function": { title: WebInspector.UIString("Function"), disclosure: true, sortable: true } }; @@ -75,16 +77,14 @@ WebInspector.ProfileView = function(profile) this.resetButton.className = "reset-profile-status-bar-item status-bar-item hidden"; this.resetButton.addEventListener("click", this._resetClicked.bind(this), false); - // Default to the heavy profile. - profile = profile.heavyProfile; + this.profile = profile; - // By default the profile isn't sorted, so sort based on our default sort - // column and direction added to the DataGrid columns above. - profile.sortSelfTimeDescending(); + this.profileDataGridTree = this.bottomUpProfileDataGridTree; + this.profileDataGridTree.sort(WebInspector.ProfileDataGridTree.propertyComparator("selfTime", false)); - this._updatePercentButton(); + this.refresh(); - this.profile = profile; + this._updatePercentButton(); } WebInspector.ProfileView.prototype = { @@ -101,9 +101,53 @@ WebInspector.ProfileView.prototype = { set profile(profile) { this._profile = profile; + }, + + get bottomUpProfileDataGridTree() + { + if (!this._bottomUpProfileDataGridTree) + this._bottomUpProfileDataGridTree = new WebInspector.BottomUpProfileDataGridTree(this, this.profile.head); + return this._bottomUpProfileDataGridTree; + }, + + get topDownProfileDataGridTree() + { + if (!this._topDownProfileDataGridTree) + this._topDownProfileDataGridTree = new WebInspector.TopDownProfileDataGridTree(this, this.profile.head); + return this._topDownProfileDataGridTree; + }, + + get currentTree() + { + return this._currentTree; + }, + + set currentTree(tree) + { + this._currentTree = tree; this.refresh(); }, + get topDownTree() + { + if (!this._topDownTree) { + this._topDownTree = WebInspector.TopDownTreeFactory.create(this.profile.head); + this._sortProfile(this._topDownTree); + } + + return this._topDownTree; + }, + + get bottomUpTree() + { + if (!this._bottomUpTree) { + this._bottomUpTree = WebInspector.BottomUpTreeFactory.create(this.profile.head); + this._sortProfile(this._bottomUpTree); + } + + return this._bottomUpTree; + }, + hide: function() { WebInspector.View.prototype.hide.call(this); @@ -116,20 +160,18 @@ WebInspector.ProfileView.prototype = { this.dataGrid.removeChildren(); - var children = this.profile.head.children; - var childrenLength = children.length; - for (var i = 0; i < childrenLength; ++i) - if (children[i].visible) - this.dataGrid.appendChild(new WebInspector.ProfileDataGridNode(this, children[i])); + var children = this.profileDataGridTree.children; + var count = children.length; + + for (var index = 0; index < count; ++index) + this.dataGrid.appendChild(children[index]); if (selectedProfileNode && selectedProfileNode._dataGridNode) selectedProfileNode._dataGridNode.selected = true; }, - refreshShowAsPercents: function() + refreshVisibleData: function() { - this._updatePercentButton(); - var child = this.dataGrid.children[0]; while (child) { child.refresh(); @@ -137,6 +179,12 @@ WebInspector.ProfileView.prototype = { } }, + refreshShowAsPercents: function() + { + this._updatePercentButton(); + this.refreshVisibleData(); + }, + searchCanceled: function() { if (this._searchResults) { @@ -191,71 +239,90 @@ WebInspector.ProfileView.prototype = { if (!isNaN(queryNumber) && !(greaterThan || lessThan)) equalTo = true; - function matchesQuery(profileNode) + function matchesQuery(/*ProfileDataGridNode*/ profileDataGridNode) { - delete profileNode._searchMatchedSelfColumn; - delete profileNode._searchMatchedTotalColumn; - delete profileNode._searchMatchedCallsColumn; - delete profileNode._searchMatchedFunctionColumn; + delete profileDataGridNode._searchMatchedSelfColumn; + delete profileDataGridNode._searchMatchedTotalColumn; + delete profileDataGridNode._searchMatchedAverageColumn; + delete profileDataGridNode._searchMatchedCallsColumn; + delete profileDataGridNode._searchMatchedFunctionColumn; if (percentUnits) { if (lessThan) { - if (profileNode.selfPercent < queryNumber) - profileNode._searchMatchedSelfColumn = true; - if (profileNode.totalPercent < queryNumber) - profileNode._searchMatchedTotalColumn = true; + if (profileDataGridNode.selfPercent < queryNumber) + profileDataGridNode._searchMatchedSelfColumn = true; + if (profileDataGridNode.totalPercent < queryNumber) + profileDataGridNode._searchMatchedTotalColumn = true; + if (profileDataGridNode.averagePercent < queryNumberMilliseconds) + profileDataGridNode._searchMatchedAverageColumn = true; } else if (greaterThan) { - if (profileNode.selfPercent > queryNumber) - profileNode._searchMatchedSelfColumn = true; - if (profileNode.totalPercent > queryNumber) - profileNode._searchMatchedTotalColumn = true; + if (profileDataGridNode.selfPercent > queryNumber) + profileDataGridNode._searchMatchedSelfColumn = true; + if (profileDataGridNode.totalPercent > queryNumber) + profileDataGridNode._searchMatchedTotalColumn = true; + if (profileDataGridNode.averagePercent < queryNumberMilliseconds) + profileDataGridNode._searchMatchedAverageColumn = true; } if (equalTo) { - if (profileNode.selfPercent == queryNumber) - profileNode._searchMatchedSelfColumn = true; - if (profileNode.totalPercent == queryNumber) - profileNode._searchMatchedTotalColumn = true; + if (profileDataGridNode.selfPercent == queryNumber) + profileDataGridNode._searchMatchedSelfColumn = true; + if (profileDataGridNode.totalPercent == queryNumber) + profileDataGridNode._searchMatchedTotalColumn = true; + if (profileDataGridNode.averagePercent < queryNumberMilliseconds) + profileDataGridNode._searchMatchedAverageColumn = true; } } else if (millisecondsUnits || secondsUnits) { if (lessThan) { - if (profileNode.selfTime < queryNumberMilliseconds) - profileNode._searchMatchedSelfColumn = true; - if (profileNode.totalTime < queryNumberMilliseconds) - profileNode._searchMatchedTotalColumn = true; + if (profileDataGridNode.selfTime < queryNumberMilliseconds) + profileDataGridNode._searchMatchedSelfColumn = true; + if (profileDataGridNode.totalTime < queryNumberMilliseconds) + profileDataGridNode._searchMatchedTotalColumn = true; + if (profileDataGridNode.averageTime < queryNumberMilliseconds) + profileDataGridNode._searchMatchedAverageColumn = true; } else if (greaterThan) { - if (profileNode.selfTime > queryNumberMilliseconds) - profileNode._searchMatchedSelfColumn = true; - if (profileNode.totalTime > queryNumberMilliseconds) - profileNode._searchMatchedTotalColumn = true; + if (profileDataGridNode.selfTime > queryNumberMilliseconds) + profileDataGridNode._searchMatchedSelfColumn = true; + if (profileDataGridNode.totalTime > queryNumberMilliseconds) + profileDataGridNode._searchMatchedTotalColumn = true; + if (profileDataGridNode.averageTime > queryNumberMilliseconds) + profileDataGridNode._searchMatchedAverageColumn = true; } if (equalTo) { - if (profileNode.selfTime == queryNumberMilliseconds) - profileNode._searchMatchedSelfColumn = true; - if (profileNode.totalTime == queryNumberMilliseconds) - profileNode._searchMatchedTotalColumn = true; + if (profileDataGridNode.selfTime == queryNumberMilliseconds) + profileDataGridNode._searchMatchedSelfColumn = true; + if (profileDataGridNode.totalTime == queryNumberMilliseconds) + profileDataGridNode._searchMatchedTotalColumn = true; + if (profileDataGridNode.averageTime == queryNumberMilliseconds) + profileDataGridNode._searchMatchedAverageColumn = true; } } else { - if (equalTo && profileNode.numberOfCalls == queryNumber) - profileNode._searchMatchedCallsColumn = true; - if (greaterThan && profileNode.numberOfCalls > queryNumber) - profileNode._searchMatchedCallsColumn = true; - if (lessThan && profileNode.numberOfCalls < queryNumber) - profileNode._searchMatchedCallsColumn = true; + if (equalTo && profileDataGridNode.numberOfCalls == queryNumber) + profileDataGridNode._searchMatchedCallsColumn = true; + if (greaterThan && profileDataGridNode.numberOfCalls > queryNumber) + profileDataGridNode._searchMatchedCallsColumn = true; + if (lessThan && profileDataGridNode.numberOfCalls < queryNumber) + profileDataGridNode._searchMatchedCallsColumn = true; } - if (profileNode.functionName.hasSubstring(query, true) || profileNode.url.hasSubstring(query, true)) - profileNode._searchMatchedFunctionColumn = true; - - var matched = (profileNode._searchMatchedSelfColumn || profileNode._searchMatchedTotalColumn || profileNode._searchMatchedCallsColumn || profileNode._searchMatchedFunctionColumn); - if (matched && profileNode._dataGridNode) - profileNode._dataGridNode.refresh(); + if (profileDataGridNode.functionName.hasSubstring(query, true) || profileDataGridNode.url.hasSubstring(query, true)) + profileDataGridNode._searchMatchedFunctionColumn = true; + + if (profileDataGridNode._searchMatchedSelfColumn || + profileDataGridNode._searchMatchedTotalColumn || + profileDataGridNode._searchMatchedAverageColumn || + profileDataGridNode._searchMatchedCallsColumn || + profileDataGridNode._searchMatchedFunctionColumn); + { + profileDataGridNode.refresh(); + return true; + } - return matched; + return false; } - var current = this.profile.head; + var current = this.dataGrid; var ancestors = []; var nextIndexes = []; var startIndex = 0; @@ -380,12 +447,12 @@ WebInspector.ProfileView.prototype = { return; if (event.target.selectedIndex == 1 && this.view == "Heavy") { - this._sortProfile(this.profile.treeProfile); - this.profile = this.profile.treeProfile; + this.profileDataGridTree = this.topDownProfileDataGridTree; + this._sortProfile(); this.view = "Tree"; } else if (event.target.selectedIndex == 0 && this.view == "Tree") { - this._sortProfile(this.profile.heavyProfile); - this.profile = this.profile.heavyProfile; + this.profileDataGridTree = this.bottomUpProfileDataGridTree; + this._sortProfile(); this.view = "Heavy"; } @@ -401,15 +468,16 @@ WebInspector.ProfileView.prototype = { _percentClicked: function(event) { - var currentState = this.showSelfTimeAsPercent && this.showTotalTimeAsPercent; + var currentState = this.showSelfTimeAsPercent && this.showTotalTimeAsPercent && this.showAverageTimeAsPercent; this.showSelfTimeAsPercent = !currentState; this.showTotalTimeAsPercent = !currentState; + this.showAverageTimeAsPercent = !currentState; this.refreshShowAsPercents(); }, _updatePercentButton: function() { - if (this.showSelfTimeAsPercent && this.showTotalTimeAsPercent) { + if (this.showSelfTimeAsPercent && this.showTotalTimeAsPercent && this.showAverageTimeAsPercent) { this.percentButton.title = WebInspector.UIString("Show absolute total and self times."); this.percentButton.addStyleClass("toggled-on"); } else { @@ -420,28 +488,36 @@ WebInspector.ProfileView.prototype = { _focusClicked: function(event) { - if (!this.dataGrid.selectedNode || !this.dataGrid.selectedNode.profileNode) + if (!this.dataGrid.selectedNode) return; + this.resetButton.removeStyleClass("hidden"); - this.profile.focus(this.dataGrid.selectedNode.profileNode); + this.profileDataGridTree.focus(this.dataGrid.selectedNode); this.refresh(); + this.refreshVisibleData(); }, _excludeClicked: function(event) { - if (!this.dataGrid.selectedNode || !this.dataGrid.selectedNode.profileNode) + var selectedNode = this.dataGrid.selectedNode + + if (!selectedNode) return; + + selectedNode.deselect(); + this.resetButton.removeStyleClass("hidden"); - this.profile.exclude(this.dataGrid.selectedNode.profileNode); - this.dataGrid.selectedNode.deselect(); + this.profileDataGridTree.exclude(selectedNode); this.refresh(); + this.refreshVisibleData(); }, _resetClicked: function(event) { this.resetButton.addStyleClass("hidden"); - this.profile.restoreAll(); + this.profileDataGridTree.restore(); this.refresh(); + this.refreshVisibleData(); }, _dataGridNodeSelected: function(node) @@ -461,37 +537,21 @@ WebInspector.ProfileView.prototype = { this._sortProfile(this.profile); }, - _sortProfile: function(profile) + _sortProfile: function() { - if (!profile) - return; - - var sortOrder = this.dataGrid.sortOrder; + var sortAscending = this.dataGrid.sortOrder === "ascending"; var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier; + var sortProperty = { + "average": "averageTime", + "self": "selfTime", + "total": "totalTime", + "calls": "numberOfCalls", + "function": "functionName" + }[sortColumnIdentifier]; - var sortingFunctionName = "sort"; - - if (sortColumnIdentifier === "self") - sortingFunctionName += "SelfTime"; - else if (sortColumnIdentifier === "total") - sortingFunctionName += "TotalTime"; - else if (sortColumnIdentifier === "calls") - sortingFunctionName += "Calls"; - else if (sortColumnIdentifier === "function") - sortingFunctionName += "FunctionName"; - - if (sortOrder === "ascending") - sortingFunctionName += "Ascending"; - else - sortingFunctionName += "Descending"; - - if (!(sortingFunctionName in this.profile)) - return; - - profile[sortingFunctionName](); + this.profileDataGridTree.sort(WebInspector.ProfileDataGridTree.propertyComparator(sortProperty, sortAscending)); - if (profile === this.profile) - this.refresh(); + this.refresh(); }, _mouseDownInDataGrid: function(event) @@ -500,13 +560,15 @@ WebInspector.ProfileView.prototype = { return; var cell = event.target.enclosingNodeOrSelfWithNodeName("td"); - if (!cell || (!cell.hasStyleClass("total-column") && !cell.hasStyleClass("self-column"))) + if (!cell || (!cell.hasStyleClass("total-column") && !cell.hasStyleClass("self-column") && !cell.hasStyleClass("average-column"))) return; if (cell.hasStyleClass("total-column")) this.showTotalTimeAsPercent = !this.showTotalTimeAsPercent; else if (cell.hasStyleClass("self-column")) this.showSelfTimeAsPercent = !this.showSelfTimeAsPercent; + else if (cell.hasStyleClass("average-column")) + this.showAverageTimeAsPercent = !this.showAverageTimeAsPercent; this.refreshShowAsPercents(); @@ -516,127 +578,3 @@ WebInspector.ProfileView.prototype = { } WebInspector.ProfileView.prototype.__proto__ = WebInspector.View.prototype; - -WebInspector.ProfileDataGridNode = function(profileView, profileNode) -{ - this.profileView = profileView; - - this.profileNode = profileNode; - profileNode._dataGridNode = this; - - // Find the first child that is visible. Since we don't want to claim - // we have children if all the children are invisible. - var hasChildren = false; - var children = this.profileNode.children; - var childrenLength = children.length; - for (var i = 0; i < childrenLength; ++i) { - if (children[i].visible) { - hasChildren = true; - break; - } - } - - WebInspector.DataGridNode.call(this, null, hasChildren); - - this.addEventListener("populate", this._populate, this); - - this.expanded = profileNode._expanded; -} - -WebInspector.ProfileDataGridNode.prototype = { - get data() - { - function formatMilliseconds(time) - { - return Number.secondsToString(time / 1000, WebInspector.UIString.bind(WebInspector), true); - } - - var data = {}; - data["function"] = this.profileNode.functionName; - data["calls"] = this.profileNode.numberOfCalls; - - if (this.profileView.showSelfTimeAsPercent) - data["self"] = WebInspector.UIString("%.2f%%", this.profileNode.selfPercent); - else - data["self"] = formatMilliseconds(this.profileNode.selfTime); - - if (this.profileView.showTotalTimeAsPercent) - data["total"] = WebInspector.UIString("%.2f%%", this.profileNode.totalPercent); - else - data["total"] = formatMilliseconds(this.profileNode.totalTime); - - return data; - }, - - createCell: function(columnIdentifier) - { - var cell = WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier); - - if (columnIdentifier === "self" && this.profileNode._searchMatchedSelfColumn) - cell.addStyleClass("highlight"); - else if (columnIdentifier === "total" && this.profileNode._searchMatchedTotalColumn) - cell.addStyleClass("highlight"); - else if (columnIdentifier === "calls" && this.profileNode._searchMatchedCallsColumn) - cell.addStyleClass("highlight"); - - if (columnIdentifier !== "function") - return cell; - - if (this.profileNode._searchMatchedFunctionColumn) - cell.addStyleClass("highlight"); - - if (this.profileNode.url) { - var fileName = WebInspector.displayNameForURL(this.profileNode.url); - - var urlElement = document.createElement("a"); - urlElement.className = "profile-node-file webkit-html-resource-link"; - urlElement.href = this.profileNode.url; - urlElement.lineNumber = this.profileNode.lineNumber; - - if (this.profileNode.lineNumber > 0) - urlElement.textContent = fileName + ":" + this.profileNode.lineNumber; - else - urlElement.textContent = fileName; - - cell.insertBefore(urlElement, cell.firstChild); - } - - return cell; - }, - - select: function(supressSelectedEvent) - { - WebInspector.DataGridNode.prototype.select.call(this, supressSelectedEvent); - this.profileView._dataGridNodeSelected(this); - }, - - deselect: function(supressDeselectedEvent) - { - WebInspector.DataGridNode.prototype.deselect.call(this, supressDeselectedEvent); - this.profileView._dataGridNodeDeselected(this); - }, - - expand: function() - { - WebInspector.DataGridNode.prototype.expand.call(this); - this.profileNode._expanded = true; - }, - - collapse: function() - { - WebInspector.DataGridNode.prototype.collapse.call(this); - this.profileNode._expanded = false; - }, - - _populate: function(event) - { - var children = this.profileNode.children; - var childrenLength = children.length; - for (var i = 0; i < childrenLength; ++i) - if (children[i].visible) - this.appendChild(new WebInspector.ProfileDataGridNode(this.profileView, children[i])); - this.removeEventListener("populate", this._populate, this); - } -} - -WebInspector.ProfileDataGridNode.prototype.__proto__ = WebInspector.DataGridNode.prototype; diff --git a/WebCore/inspector/front-end/ProfilesPanel.js b/WebCore/inspector/front-end/ProfilesPanel.js index ea3b85c..aafc306 100644 --- a/WebCore/inspector/front-end/ProfilesPanel.js +++ b/WebCore/inspector/front-end/ProfilesPanel.js @@ -218,7 +218,7 @@ WebInspector.ProfilesPanel.prototype = { view.show(this.profileViews); profile._profilesTreeElement.select(true); - profile._profilesTreeElement.reveal() + profile._profilesTreeElement.reveal(); this.visibleView = view; @@ -231,8 +231,7 @@ WebInspector.ProfilesPanel.prototype = { showView: function(view) { - // Always use the treeProfile, since the heavy profile might be showing. - this.showProfile(view.profile.treeProfile); + this.showProfile(view.profile); }, profileViewForProfile: function(profile) @@ -295,8 +294,7 @@ WebInspector.ProfilesPanel.prototype = { searchMatchFound: function(view, matches) { - // Always use the treeProfile, since the heavy profile might be showing. - view.profile.treeProfile._profilesTreeElement.searchMatches = matches; + view.profile._profilesTreeElement.searchMatches = matches; }, searchCanceled: function(startingNewSearch) @@ -356,15 +354,15 @@ WebInspector.ProfilesPanel.prototype = { { if (InspectorController.profilerEnabled()) return; - this._toggleProfiling(); + this._toggleProfiling(this.panelEnablerView.alwaysEnabled); }, - _toggleProfiling: function() + _toggleProfiling: function(optionalAlways) { if (InspectorController.profilerEnabled()) - InspectorController.disableProfiler(); + InspectorController.disableProfiler(true); else - InspectorController.enableProfiler(); + InspectorController.enableProfiler(!!optionalAlways); }, _populateProfiles: function() diff --git a/WebCore/inspector/front-end/ResourcesPanel.js b/WebCore/inspector/front-end/ResourcesPanel.js index e02baf3..04d998e 100644 --- a/WebCore/inspector/front-end/ResourcesPanel.js +++ b/WebCore/inspector/front-end/ResourcesPanel.js @@ -126,6 +126,19 @@ WebInspector.ResourcesPanel = function() this.resourcesTreeElement.expand(); + var panelEnablerHeading = WebInspector.UIString("You need to enable resource tracking to use the this panel."); + var panelEnablerDisclaimer = WebInspector.UIString("Enabling resource tracking will reload the page and make page loading slower."); + var panelEnablerButton = WebInspector.UIString("Enable resource tracking"); + + this.panelEnablerView = new WebInspector.PanelEnablerView("resources", panelEnablerHeading, panelEnablerDisclaimer, panelEnablerButton); + this.panelEnablerView.addEventListener("enable clicked", this._enableResourceTracking, this); + + this.element.appendChild(this.panelEnablerView.element); + + this.enableToggleButton = document.createElement("button"); + this.enableToggleButton.className = "enable-toggle-status-bar-item status-bar-item"; + this.enableToggleButton.addEventListener("click", this._toggleResourceTracking.bind(this), false); + this.largerResourcesButton = document.createElement("button"); this.largerResourcesButton.id = "resources-larger-resources-status-bar-item"; this.largerResourcesButton.className = "status-bar-item toggled-on"; @@ -151,7 +164,7 @@ WebInspector.ResourcesPanel.prototype = { get statusBarItems() { - return [this.largerResourcesButton, this.sortingSelectElement]; + return [this.enableToggleButton, this.largerResourcesButton, this.sortingSelectElement]; }, show: function() @@ -358,6 +371,16 @@ WebInspector.ResourcesPanel.prototype = { this._updateSummaryGraph(); }, + resourceTrackingWasEnabled: function() + { + this.reset(); + }, + + resourceTrackingWasDisabled: function() + { + this.reset(); + }, + reset: function() { this.closeVisibleResource(); @@ -394,6 +417,20 @@ WebInspector.ResourcesPanel.prototype = { this._updateGraphDividersIfNeeded(true); this._drawSummaryGraph(); // draws an empty graph + + if (InspectorController.resourceTrackingEnabled()) { + this.enableToggleButton.title = WebInspector.UIString("Resource tracking enabled. Click to disable."); + this.enableToggleButton.addStyleClass("toggled-on"); + this.largerResourcesButton.removeStyleClass("hidden"); + this.sortingSelectElement.removeStyleClass("hidden"); + this.panelEnablerView.visible = false; + } else { + this.enableToggleButton.title = WebInspector.UIString("Resource tracking disabled. Click to enable."); + this.enableToggleButton.removeStyleClass("toggled-on"); + this.largerResourcesButton.addStyleClass("hidden"); + this.sortingSelectElement.addStyleClass("hidden"); + this.panelEnablerView.visible = true; + } }, addResource: function(resource) @@ -1085,6 +1122,26 @@ WebInspector.ResourcesPanel.prototype = { var visibleView = this.visibleView; if (visibleView && "resize" in visibleView) visibleView.resize(); + }, + + _enableResourceTracking: function() + { + if (InspectorController.resourceTrackingEnabled()) + return; + this._toggleResourceTracking(this.panelEnablerView.alwaysEnabled); + }, + + _toggleResourceTracking: function(optionalAlways) + { + if (InspectorController.resourceTrackingEnabled()) { + this.largerResourcesButton.visible = false; + this.sortingSelectElement.visible = false; + InspectorController.disableResourceTracking(true); + } else { + this.largerResourcesButton.visible = true; + this.sortingSelectElement.visible = true; + InspectorController.enableResourceTracking(!!optionalAlways); + } } } diff --git a/WebCore/inspector/front-end/ScriptsPanel.js b/WebCore/inspector/front-end/ScriptsPanel.js index 2792834..7af9292 100644 --- a/WebCore/inspector/front-end/ScriptsPanel.js +++ b/WebCore/inspector/front-end/ScriptsPanel.js @@ -205,6 +205,10 @@ WebInspector.ScriptsPanel.prototype = { continue; view.visible = false; } + if (this._attachDebuggerWhenShown) { + InspectorController.enableDebuggerFromFrontend(false); + delete this._attachDebuggerWhenShown; + } }, get searchableViews() @@ -359,6 +363,24 @@ WebInspector.ScriptsPanel.prototype = { window.focus(); }, + debuggerResumed: function() + { + this._paused = false; + this._waitingToPause = false; + this._stepping = false; + + this._clearInterface(); + }, + + attachDebuggerWhenShown: function() + { + if (this.element.parentElement) { + InspectorController.enableDebuggerFromFrontend(false); + } else { + this._attachDebuggerWhenShown = true; + } + }, + debuggerWasEnabled: function() { this.reset(); @@ -573,21 +595,25 @@ WebInspector.ScriptsPanel.prototype = { option.representedObject = (script.resource || script); option.text = (script.sourceURL ? WebInspector.displayNameForURL(script.sourceURL) : WebInspector.UIString("(program)")); - var insertionIndex = -1; - if (select.childNodes) { - insertionIndex = insertionIndexForObjectInListSortedByFunction(option, select.childNodes, function(a, b) { - a = a.text.toLowerCase(); - b = b.text.toLowerCase(); - - if (a < b) - return -1; - else if (a > b) - return 1; - - return 0; - }); + function optionCompare(a, b) + { + var aTitle = a.text.toLowerCase(); + var bTitle = b.text.toLowerCase(); + if (aTitle < bTitle) + return -1; + else if (aTitle > bTitle) + return 1; + + var aSourceID = a.representedObject.sourceID; + var bSourceID = b.representedObject.sourceID; + if (aSourceID < bSourceID) + return -1; + else if (aSourceID > bSourceID) + return 1; + return 0; } + var insertionIndex = insertionIndexForObjectInListSortedByFunction(option, select.childNodes, optionCompare); if (insertionIndex < 0) select.appendChild(option); else @@ -758,19 +784,19 @@ WebInspector.ScriptsPanel.prototype = { { if (InspectorController.debuggerEnabled()) return; - this._toggleDebugging(); + this._toggleDebugging(this.panelEnablerView.alwaysEnabled); }, - _toggleDebugging: function() + _toggleDebugging: function(optionalAlways) { this._paused = false; this._waitingToPause = false; this._stepping = false; if (InspectorController.debuggerEnabled()) - InspectorController.disableDebugger(); + InspectorController.disableDebugger(true); else - InspectorController.enableDebugger(); + InspectorController.enableDebuggerFromFrontend(!!optionalAlways); }, _togglePauseOnExceptions: function() diff --git a/WebCore/inspector/front-end/SourceFrame.js b/WebCore/inspector/front-end/SourceFrame.js index 26c0626..18d9073 100644 --- a/WebCore/inspector/front-end/SourceFrame.js +++ b/WebCore/inspector/front-end/SourceFrame.js @@ -110,6 +110,11 @@ WebInspector.SourceFrame.prototype = { revealLine: function(lineNumber) { + if (!this._isContentLoaded()) { + this._lineNumberToReveal = lineNumber; + return; + } + var row = this.sourceRow(lineNumber); if (row) row.scrollIntoViewIfNeeded(true); @@ -172,6 +177,11 @@ WebInspector.SourceFrame.prototype = { highlightLine: function(lineNumber) { + if (!this._isContentLoaded()) { + this._lineNumberToHighlight = lineNumber; + return; + } + var sourceRow = this.sourceRow(lineNumber); if (!sourceRow) return; @@ -233,9 +243,28 @@ WebInspector.SourceFrame.prototype = { this._addExistingMessagesToSource(); this._addExistingBreakpointsToSource(); this._updateExecutionLine(); + if (this._executionLine) + this.revealLine(this._executionLine); if (this.autoSizesToFitContentHeight) this.sizeToFitContentHeight(); + + if (this._lineNumberToReveal) { + this.revealLine(this._lineNumberToReveal); + delete this._lineNumberToReveal; + } + + if (this._lineNumberToHighlight) { + this.highlightLine(this._lineNumberToHighlight); + delete this._lineNumberToHighlight; + } + + this.dispatchEventToListeners("content loaded"); + }, + + _isContentLoaded: function() { + var doc = this.element.contentDocument; + return doc && doc.getElementsByTagName("table")[0]; }, _windowResized: function(event) diff --git a/WebCore/inspector/front-end/SourceView.js b/WebCore/inspector/front-end/SourceView.js index 309027e..7510c8c 100644 --- a/WebCore/inspector/front-end/SourceView.js +++ b/WebCore/inspector/front-end/SourceView.js @@ -95,10 +95,15 @@ WebInspector.SourceView.prototype = { this.attach(); - if (!InspectorController.addResourceSourceToFrame(this.resource.identifier, this.sourceFrame.element)) - return; - delete this._frameNeedsSetup; + this.sourceFrame.addEventListener("content loaded", this._contentLoaded, this); + InspectorController.addResourceSourceToFrame(this.resource.identifier, this.sourceFrame.element); + }, + + _contentLoaded: function() + { + delete this._frameNeedsSetup; + this.sourceFrame.removeEventListener("content loaded", this._contentLoaded, this); if (this.resource.type === WebInspector.Resource.Type.Script) { this.sourceFrame.addEventListener("syntax highlighting complete", this._syntaxHighlightingComplete, this); @@ -274,7 +279,7 @@ WebInspector.SourceView.prototype = { if (!foundRange) return; - var selection = window.getSelection(); + var selection = this.sourceFrame.element.contentWindow.getSelection(); selection.removeAllRanges(); selection.addRange(foundRange); diff --git a/WebCore/inspector/front-end/StylesSidebarPane.js b/WebCore/inspector/front-end/StylesSidebarPane.js index f0a9afb..c30444b 100644 --- a/WebCore/inspector/front-end/StylesSidebarPane.js +++ b/WebCore/inspector/front-end/StylesSidebarPane.js @@ -586,8 +586,8 @@ WebInspector.StylePropertyTreeElement.prototype = { this.listItemElement.appendChild(document.createTextNode(";")); if (value) { - // FIXME: this dosen't catch keyword based colors like black and white - var colors = value.match(/((rgb|hsl)a?\([^)]+\))|(#[0-9a-fA-F]{6})|(#[0-9a-fA-F]{3})/g); + // FIXME: this only covers W3C and CSS 16 valid color names + var colors = value.match(/((rgb|hsl)a?\([^)]+\))|(#[0-9a-fA-F]{6})|(#[0-9a-fA-F]{3})|aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|purple|red|silver|teal|white|yellow/g); if (colors) { var colorsLength = colors.length; for (var i = 0; i < colorsLength; ++i) { diff --git a/WebCore/inspector/front-end/TopDownProfileDataGridTree.js b/WebCore/inspector/front-end/TopDownProfileDataGridTree.js new file mode 100644 index 0000000..b9d8b94 --- /dev/null +++ b/WebCore/inspector/front-end/TopDownProfileDataGridTree.js @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2009 280 North Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +WebInspector.TopDownProfileDataGridNode = function(/*ProfileView*/ profileView, /*ProfileNode*/ profileNode, /*TopDownProfileDataGridTree*/ owningTree) +{ + var hasChildren = (profileNode.children && profileNode.children.length); + + WebInspector.ProfileDataGridNode.call(this, profileView, profileNode, owningTree, hasChildren); + + this._remainingChildren = profileNode.children; +} + +WebInspector.TopDownProfileDataGridNode.prototype = { + _populate: function(event) + { + var children = this._remainingChildren; + var childrenLength = children.length; + + for (var i = 0; i < childrenLength; ++i) + this.appendChild(new WebInspector.TopDownProfileDataGridNode(this.profileView, children[i], this.tree)); + + if (this.removeEventListener) + this.removeEventListener("populate", this._populate, this); + + this._remainingChildren = null; + }, + + _exclude: function(aCallUID) + { + if (this._remainingChildren) + this._populate(); + + this._save(); + + var children = this.children; + var index = this.children.length; + + while (index--) + children[index]._exclude(aCallUID); + + var child = this.childrenByCallUID[aCallUID]; + + if (child) + this._merge(child, true); + } +} + +WebInspector.TopDownProfileDataGridNode.prototype.__proto__ = WebInspector.ProfileDataGridNode.prototype; + +WebInspector.TopDownProfileDataGridTree = function(/*ProfileView*/ profileView, /*ProfileNode*/ profileNode) +{ + WebInspector.ProfileDataGridTree.call(this, profileView, profileNode); + + this._remainingChildren = profileNode.children; + + WebInspector.TopDownProfileDataGridNode.prototype._populate.call(this); +} + +WebInspector.TopDownProfileDataGridTree.prototype = { + focus: function(/*ProfileDataGridNode*/ profileDataGrideNode) + { + if (!profileDataGrideNode) + return; + + this._save(); + + this.children = [profileDataGrideNode]; + this.totalTime = profileDataGrideNode.totalTime; + }, + + exclude: function(/*ProfileDataGridNode*/ profileDataGrideNode) + { + if (!profileDataGrideNode) + return; + + this._save(); + + var excludedCallUID = profileDataGrideNode.callUID; + + WebInspector.TopDownProfileDataGridNode.prototype._exclude.call(this, excludedCallUID); + + if (this.lastComparator) + this.sort(this.lastComparator, true); + }, + + _merge: WebInspector.TopDownProfileDataGridNode.prototype._merge +} + +WebInspector.TopDownProfileDataGridTree.prototype.__proto__ = WebInspector.ProfileDataGridTree.prototype; diff --git a/WebCore/inspector/front-end/WebKit.qrc b/WebCore/inspector/front-end/WebKit.qrc index 997d4a7..76a925b 100644 --- a/WebCore/inspector/front-end/WebKit.qrc +++ b/WebCore/inspector/front-end/WebKit.qrc @@ -1,6 +1,7 @@ <!DOCTYPE RCC><RCC version="1.0"> <qresource prefix="/webkit/inspector"> <file>inspector.html</file> + <file>BottomUpProfileDataGridTree.js</file> <file>Breakpoint.js</file> <file>BreakpointsSidebarPane.js</file> <file>CallStackSidebarPane.js</file> @@ -24,6 +25,7 @@ <file>Panel.js</file> <file>PanelEnablerView.js</file> <file>Placard.js</file> + <file>ProfileDataGridTree.js</file> <file>ProfilesPanel.js</file> <file>ProfileView.js</file> <file>PropertiesSection.js</file> @@ -42,6 +44,7 @@ <file>SourceView.js</file> <file>StylesSidebarPane.js</file> <file>TextPrompt.js</file> + <file>TopDownProfileDataGridTree.js</file> <file>treeoutline.js</file> <file>utilities.js</file> <file>View.js</file> @@ -96,6 +99,7 @@ <file>Images/profilesIcon.png</file> <file>Images/profileSmallIcon.png</file> <file>Images/profilesSilhouette.png</file> + <file>Images/radioDot.png</file> <file>Images/recordButtons.png</file> <file>Images/reloadButtons.png</file> <file>Images/resourceCSSIcon.png</file> @@ -155,6 +159,7 @@ <file>Images/treeUpTriangleWhite.png</file> <file>Images/userInputIcon.png</file> <file>Images/userInputPreviousIcon.png</file> + <file>Images/userInputResultIcon.png</file> <file>Images/warningIcon.png</file> <file>Images/warningMediumIcon.png</file> <file>Images/warningsErrors.png</file> diff --git a/WebCore/inspector/front-end/inspector.css b/WebCore/inspector/front-end/inspector.css index 082955e..2653781 100644 --- a/WebCore/inspector/front-end/inspector.css +++ b/WebCore/inspector/front-end/inspector.css @@ -1919,6 +1919,7 @@ body.inactive .data-grid th.sort-ascending, body.inactive .data-grid th.sort-des font-size: 10px; color: rgb(110, 116, 128); margin-bottom: 12px; + margin-left: 20px; } .panel-enabler-disclaimer:empty { @@ -1954,10 +1955,8 @@ body.inactive .data-grid th.sort-ascending, body.inactive .data-grid th.sort-des position: relative; display: block; text-align: left; - margin-left: 50px; - margin-bottom: 6px; - line-height: 18px; word-break: break-word; + margin: 0 0 5px 20px; } .panel-enabler-view button { @@ -1986,6 +1985,30 @@ body.inactive .panel-enabler-view button, .panel-enabler-view button:disabled { background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(250, 250, 250)), to(rgb(235, 235, 235))); } +.panel-enabler-view input { + height: 17px; + width: 17px; + border: 1px solid rgb(165, 165, 165); + background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(252, 252, 252)), to(rgb(223, 223, 223))); + -webkit-border-radius: 8px; + -webkit-appearance: none; + vertical-align: middle; + margin: 0 5px 5px 0; +} + +.panel-enabler-view input:active { + background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(194, 194, 194)), to(rgb(239, 239, 239))); +} + +.panel-enabler-view input:checked { + background: url(Images/radioDot.png) center no-repeat, + -webkit-gradient(linear, left top, left bottom, from(rgb(252, 252, 252)), to(rgb(223, 223, 223))); +} + +.panel-enabler-view.resources img { + content: url(Images/scriptsSilhouette.png); +} + .panel-enabler-view.scripts img { content: url(Images/scriptsSilhouette.png); } @@ -2930,6 +2953,14 @@ body.inactive .sidebar-tree-item.selected .bubble.search-matches { height: 100%; } +.profile-view .data-grid th.average-column { + text-align: center; +} + +.profile-view .data-grid td.average-column { + text-align: right; +} + .profile-view .data-grid th.self-column { text-align: center; } diff --git a/WebCore/inspector/front-end/inspector.html b/WebCore/inspector/front-end/inspector.html index 77d720b..184bb45 100644 --- a/WebCore/inspector/front-end/inspector.html +++ b/WebCore/inspector/front-end/inspector.html @@ -73,6 +73,9 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <script type="text/javascript" src="DatabaseTableView.js"></script> <script type="text/javascript" src="DatabaseQueryView.js"></script> <script type="text/javascript" src="ScriptView.js"></script> + <script type="text/javascript" src="ProfileDataGridTree.js"></script> + <script type="text/javascript" src="BottomUpProfileDataGridTree.js"></script> + <script type="text/javascript" src="TopDownProfileDataGridTree.js"></script> <script type="text/javascript" src="ProfileView.js"></script> </head> <body class="detached"> diff --git a/WebCore/inspector/front-end/inspector.js b/WebCore/inspector/front-end/inspector.js index 90ffa2b..9d5bac0 100644 --- a/WebCore/inspector/front-end/inspector.js +++ b/WebCore/inspector/front-end/inspector.js @@ -41,7 +41,7 @@ var Preferences = { } var WebInspector = { - resources: [], + resources: {}, resourceURLMap: {}, missingLocalizedStrings: {}, @@ -279,22 +279,24 @@ WebInspector.loaded = function() document.body.addStyleClass("platform-" + platform); this.console = new WebInspector.Console(); - this.panels = { - elements: new WebInspector.ElementsPanel(), - resources: new WebInspector.ResourcesPanel(), - scripts: new WebInspector.ScriptsPanel(), - profiles: new WebInspector.ProfilesPanel(), - databases: new WebInspector.DatabasesPanel() - }; + this.panels = {}; var hiddenPanels = (InspectorController.hiddenPanels() || "").split(','); + if (hiddenPanels.indexOf("elements") === -1) + this.panels.elements = new WebInspector.ElementsPanel(); + if (hiddenPanels.indexOf("resources") === -1) + this.panels.resources = new WebInspector.ResourcesPanel(); + if (hiddenPanels.indexOf("scripts") === -1) + this.panels.scripts = new WebInspector.ScriptsPanel(); + if (hiddenPanels.indexOf("profiles") === -1) + this.panels.profiles = new WebInspector.ProfilesPanel(); + if (hiddenPanels.indexOf("databases") === -1) + this.panels.databases = new WebInspector.DatabasesPanel(); var toolbarElement = document.getElementById("toolbar"); var previousToolbarItem = toolbarElement.children[0]; for (var panelName in this.panels) { - if (hiddenPanels.indexOf(panelName) !== -1) - continue; var panel = this.panels[panelName]; var panelToolbarItem = panel.toolbarItem; panelToolbarItem.addEventListener("click", this._toolbarItemClicked.bind(this)); @@ -387,6 +389,12 @@ var windowLoaded = function() window.addEventListener("load", windowLoaded, false); +WebInspector.dispatch = function() { + var methodName = arguments[0]; + var parameters = Array.prototype.slice.call(arguments, 1); + WebInspector[methodName].apply(this, parameters); +} + WebInspector.windowUnload = function(event) { InspectorController.windowUnloading(); @@ -769,9 +777,18 @@ WebInspector.showDatabasesPanel = function() this.currentPanel = this.panels.databases; } -WebInspector.addResource = function(resource) +WebInspector.addResource = function(identifier, payload) { - this.resources.push(resource); + var resource = new WebInspector.Resource( + payload.requestHeaders, + payload.requestURL, + payload.host, + payload.path, + payload.lastPathComponent, + identifier, + payload.isMainResource, + payload.cached); + this.resources[identifier] = resource; this.resourceURLMap[resource.url] = resource; if (resource.mainResource) { @@ -783,27 +800,101 @@ WebInspector.addResource = function(resource) this.panels.resources.addResource(resource); } -WebInspector.removeResource = function(resource) +WebInspector.updateResource = function(identifier, payload) { + var resource = this.resources[identifier]; + if (!resource) + return; + + if (payload.didRequestChange) { + resource.url = payload.url; + resource.domain = payload.domain; + resource.path = payload.path; + resource.lastPathComponent = payload.lastPathComponent; + resource.requestHeaders = payload.requestHeaders; + resource.mainResource = payload.mainResource; + } + + if (payload.didResponseChange) { + resource.mimeType = payload.mimeType; + resource.suggestedFilename = payload.suggestedFilename; + resource.expectedContentLength = payload.expectedContentLength; + resource.statusCode = payload.statusCode; + resource.suggestedFilename = payload.suggestedFilename; + resource.responseHeaders = payload.responseHeaders; + } + + if (payload.didTypeChange) { + resource.type = payload.type; + } + + if (payload.didLengthChange) { + resource.contentLength = payload.contentLength; + } + + if (payload.didCompletionChange) { + resource.failed = payload.failed; + resource.finished = payload.finished; + } + + if (payload.didTimingChange) { + if (payload.startTime) + resource.startTime = payload.startTime; + if (payload.responseReceivedTime) + resource.responseReceivedTime = payload.responseReceivedTime; + if (payload.endTime) + resource.endTime = payload.endTime; + } +} + +WebInspector.removeResource = function(identifier) +{ + var resource = this.resources[identifier]; + if (!resource) + return; + resource.category.removeResource(resource); delete this.resourceURLMap[resource.url]; - - this.resources.remove(resource, true); + delete this.resources[identifier]; if (this.panels.resources) this.panels.resources.removeResource(resource); } -WebInspector.addDatabase = function(database) +WebInspector.addDatabase = function(payload) { + var database = new WebInspector.Database( + payload.database, + payload.domain, + payload.name, + payload.version); this.panels.databases.addDatabase(database); } -WebInspector.addDOMStorage = function(domStorage) +WebInspector.addDOMStorage = function(payload) { + var domStorage = new WebInspector.DOMStorage( + payload.domStorage, + payload.host, + payload.isLocalStorage); this.panels.databases.addDOMStorage(domStorage); } +WebInspector.resourceTrackingWasEnabled = function() +{ + this.panels.resources.resourceTrackingWasEnabled(); +} + +WebInspector.resourceTrackingWasDisabled = function() +{ + this.panels.resources.resourceTrackingWasDisabled(); +} + +WebInspector.attachDebuggerWhenShown = function() +{ + this.panels.scripts.attachDebuggerWhenShown(); +} + WebInspector.debuggerWasEnabled = function() { this.panels.scripts.debuggerWasEnabled(); @@ -839,6 +930,11 @@ WebInspector.pausedScript = function() this.panels.scripts.debuggerPaused(); } +WebInspector.resumedScript = function() +{ + this.panels.scripts.debuggerResumed(); +} + WebInspector.populateInterface = function() { for (var panelName in this.panels) { @@ -859,7 +955,7 @@ WebInspector.reset = function() for (var category in this.resourceCategories) this.resourceCategories[category].removeAllResources(); - this.resources = []; + this.resources = {}; this.resourceURLMap = {}; this.hoveredDOMNode = null; @@ -879,9 +975,17 @@ WebInspector.resourceURLChanged = function(resource, oldURL) this.resourceURLMap[resource.url] = resource; } -WebInspector.addMessageToConsole = function(msg) +WebInspector.addMessageToConsole = function(payload) { - this.console.addMessage(msg); + var consoleMessage = new WebInspector.ConsoleMessage( + payload.source, + payload.level, + payload.line, + payload.url, + payload.groupLevel, + payload.repeatCount); + consoleMessage.setMessageBody(Array.prototype.slice.call(arguments, 1)); + this.console.addMessage(consoleMessage); } WebInspector.addProfile = function(profile) @@ -1256,6 +1360,7 @@ WebInspector.MIMETypes = { "image/png": {2: true}, "image/gif": {2: true}, "image/bmp": {2: true}, + "image/vnd.microsoft.icon": {2: true}, "image/x-icon": {2: true}, "image/x-xbitmap": {2: true}, "font/ttf": {3: true}, diff --git a/WebCore/inspector/front-end/utilities.js b/WebCore/inspector/front-end/utilities.js index 7b0a20b..8fb50e2 100644 --- a/WebCore/inspector/front-end/utilities.js +++ b/WebCore/inspector/front-end/utilities.js @@ -962,10 +962,11 @@ function indexOfObjectInListSortedByFunction(anObject, aList, aFunction) else if (c < 0) last = mid - 1; else { - //we return the first occurance of an item in the list. + // Return the first occurance of an item in the list. while (mid > 0 && aFunction(anObject, aList[mid - 1]) === 0) mid--; - return mid; + first = mid; + break; } } |