summaryrefslogtreecommitdiffstats
path: root/WebCore/inspector/front-end
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2010-09-29 17:32:26 +0100
committerSteve Block <steveblock@google.com>2010-09-29 17:35:08 +0100
commit68513a70bcd92384395513322f1b801e7bf9c729 (patch)
tree161b50f75a5921d61731bb25e730005994fcec85 /WebCore/inspector/front-end
parentfd5c6425ce58eb75211be7718d5dee960842a37e (diff)
downloadexternal_webkit-68513a70bcd92384395513322f1b801e7bf9c729.zip
external_webkit-68513a70bcd92384395513322f1b801e7bf9c729.tar.gz
external_webkit-68513a70bcd92384395513322f1b801e7bf9c729.tar.bz2
Merge WebKit at r67908: Initial merge by Git
Change-Id: I43a553e7b3299b28cb6ee8aa035ed70fe342b972
Diffstat (limited to 'WebCore/inspector/front-end')
-rw-r--r--[-rwxr-xr-x]WebCore/inspector/front-end/AuditFormatters.js10
-rw-r--r--WebCore/inspector/front-end/BreakpointsSidebarPane.js11
-rw-r--r--WebCore/inspector/front-end/CallStackSidebarPane.js34
-rw-r--r--WebCore/inspector/front-end/ConsoleView.js15
-rw-r--r--WebCore/inspector/front-end/ElementsPanel.js39
-rw-r--r--WebCore/inspector/front-end/ElementsTreeOutline.js63
-rw-r--r--WebCore/inspector/front-end/HAREntry.js97
-rw-r--r--WebCore/inspector/front-end/InjectedScript.js4
-rw-r--r--WebCore/inspector/front-end/ProfileDataGridTree.js15
-rw-r--r--WebCore/inspector/front-end/Resource.js11
-rw-r--r--WebCore/inspector/front-end/ResourceView.js21
-rw-r--r--WebCore/inspector/front-end/ResourcesPanel.js45
-rw-r--r--WebCore/inspector/front-end/ScriptsPanel.js9
-rw-r--r--WebCore/inspector/front-end/Settings.js3
-rw-r--r--WebCore/inspector/front-end/StylesSidebarPane.js12
-rw-r--r--WebCore/inspector/front-end/WebKit.qrc1
-rw-r--r--WebCore/inspector/front-end/inspector.css17
-rw-r--r--WebCore/inspector/front-end/inspector.js155
-rw-r--r--WebCore/inspector/front-end/treeoutline.js1
-rw-r--r--WebCore/inspector/front-end/utilities.js9
20 files changed, 396 insertions, 176 deletions
diff --git a/WebCore/inspector/front-end/AuditFormatters.js b/WebCore/inspector/front-end/AuditFormatters.js
index de277ad..1bc1803 100755..100644
--- a/WebCore/inspector/front-end/AuditFormatters.js
+++ b/WebCore/inspector/front-end/AuditFormatters.js
@@ -79,8 +79,14 @@ WebInspector.AuditFormatters = {
return parent;
},
- url: function(url, displayText)
+ url: function(url, displayText, allowExternalNavigation)
{
- return WebInspector.linkifyURLAsNode(url, displayText || url, null, (url in WebInspector.resourceURLMap));
+ var a = document.createElement("a");
+ a.href = url;
+ a.title = url;
+ a.textContent = displayText || url;
+ if (allowExternalNavigation)
+ a.target = "_blank";
+ return a;
}
};
diff --git a/WebCore/inspector/front-end/BreakpointsSidebarPane.js b/WebCore/inspector/front-end/BreakpointsSidebarPane.js
index cda72fb..9688f3e 100644
--- a/WebCore/inspector/front-end/BreakpointsSidebarPane.js
+++ b/WebCore/inspector/front-end/BreakpointsSidebarPane.js
@@ -97,6 +97,7 @@ WebInspector.BreakpointItem = function(breakpoint)
this._breakpoint = breakpoint;
this._element = document.createElement("li");
+ this._element.addEventListener("click", this._breakpointClicked.bind(this), false);
var checkboxElement = document.createElement("input");
checkboxElement.className = "checkbox-elem";
@@ -141,8 +142,6 @@ WebInspector.JSBreakpointItem = function(breakpoint)
{
WebInspector.BreakpointItem.call(this, breakpoint);
- this._element.addEventListener("click", this._breakpointClicked.bind(this), false);
-
var displayName = this._breakpoint.url ? WebInspector.displayNameForURL(this._breakpoint.url) : WebInspector.UIString("(program)");
var labelElement = document.createTextNode(displayName + ":" + this._breakpoint.line);
this._element.appendChild(labelElement);
@@ -183,8 +182,7 @@ WebInspector.DOMBreakpointItem = function(breakpoint)
{
WebInspector.BreakpointItem.call(this, breakpoint);
- var node = WebInspector.domAgent.nodeForId(this._breakpoint.nodeId);
- var link = WebInspector.panels.elements.linkifyNodeReference(node);
+ var link = WebInspector.panels.elements.linkifyNodeById(this._breakpoint.nodeId);
this._element.appendChild(link);
var type = WebInspector.DOMBreakpoint.labelForType(this._breakpoint.type);
@@ -198,6 +196,11 @@ WebInspector.DOMBreakpointItem.prototype = {
if (this._breakpoint.type != other._breakpoint.type)
return this._breakpoint.type < other._breakpoint.type ? -1 : 1;
return 0;
+ },
+
+ _breakpointClicked: function()
+ {
+ WebInspector.updateFocusedNode(this._breakpoint.nodeId);
}
}
diff --git a/WebCore/inspector/front-end/CallStackSidebarPane.js b/WebCore/inspector/front-end/CallStackSidebarPane.js
index 60eee34..91f35a6 100644
--- a/WebCore/inspector/front-end/CallStackSidebarPane.js
+++ b/WebCore/inspector/front-end/CallStackSidebarPane.js
@@ -26,7 +26,6 @@
WebInspector.CallStackSidebarPane = function()
{
WebInspector.SidebarPane.call(this, WebInspector.UIString("Call Stack"));
-
}
WebInspector.CallStackSidebarPane.prototype = {
@@ -83,6 +82,39 @@ WebInspector.CallStackSidebarPane.prototype = {
}
},
+ updateStatus: function(status)
+ {
+ var statusElement = document.createElement("div");
+ statusElement.className = "info";
+
+ var breakpointType = status.breakpoint.type;
+ var substitutions = [WebInspector.DOMBreakpoint.labelForType(breakpointType), WebInspector.panels.elements.linkifyNodeById(status.breakpoint.nodeId)];
+ var formatters = {
+ s: function(substitution)
+ {
+ return substitution;
+ }
+ };
+ function append(a, b)
+ {
+ if (typeof b === "string")
+ b = document.createTextNode(b);
+ statusElement.appendChild(b);
+ }
+ if (breakpointType === WebInspector.DOMBreakpoint.Types.SubtreeModified) {
+ var targetNode = WebInspector.panels.elements.linkifyNodeById(status.targetNodeId);
+ if (status.insertion) {
+ if (status.targetNodeId !== status.breakpoint.nodeId)
+ WebInspector.formatLocalized("Paused on a \"%s\" breakpoint set on %s, because a new child was added to its descendant %s.", substitutions.concat(targetNode), formatters, "", append);
+ else
+ WebInspector.formatLocalized("Paused on a \"%s\" breakpoint set on %s, because a new child was added to that node.", substitutions, formatters, "", append);
+ } else
+ WebInspector.formatLocalized("Paused on a \"%s\" breakpoint set on %s, because its descendant %s was removed.", substitutions.concat(targetNode), formatters, "", append);
+ } else
+ WebInspector.formatLocalized("Paused on a \"%s\" breakpoint set on %s.", substitutions, formatters, "", append);
+ this.bodyElement.appendChild(statusElement);
+ },
+
get selectedCallFrame()
{
return this._selectedCallFrame;
diff --git a/WebCore/inspector/front-end/ConsoleView.js b/WebCore/inspector/front-end/ConsoleView.js
index 9785cd0..6f8bd8b 100644
--- a/WebCore/inspector/front-end/ConsoleView.js
+++ b/WebCore/inspector/front-end/ConsoleView.js
@@ -400,10 +400,13 @@ WebInspector.ConsoleView.prototype = {
}
var contextMenu = new WebInspector.ContextMenu();
- if (!WebInspector.monitoringXHREnabled)
- contextMenu.appendCheckboxItem(WebInspector.UIString("XMLHttpRequest logging"), InspectorBackend.enableMonitoringXHR.bind(InspectorBackend), false);
- else
- contextMenu.appendCheckboxItem(WebInspector.UIString("XMLHttpRequest logging"), InspectorBackend.disableMonitoringXHR.bind(InspectorBackend), true);
+
+ function monitoringXHRWasChanged(newState)
+ {
+ WebInspector.monitoringXHREnabled = newState;
+ }
+ var itemAction = InspectorBackend.setMonitoringXHREnabled.bind(InspectorBackend, !WebInspector.monitoringXHREnabled, monitoringXHRWasChanged);
+ contextMenu.appendCheckboxItem(WebInspector.UIString("XMLHttpRequest logging"), itemAction, WebInspector.monitoringXHREnabled);
contextMenu.appendItem(WebInspector.UIString("Clear Console"), this.requestClearMessages.bind(this));
contextMenu.show(event);
},
@@ -518,7 +521,7 @@ WebInspector.ConsoleView.prototype = {
_enterKeyPressed: function(event)
{
- if (event.altKey)
+ if (event.altKey || event.ctrlKey)
return;
event.preventDefault();
@@ -1091,7 +1094,7 @@ WebInspector.ConsoleGroup.prototype = {
if (msg.type === WebInspector.ConsoleMessage.MessageType.StartGroup || msg.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed) {
this.messagesElement.parentNode.insertBefore(element, this.messagesElement);
- element.addEventListener("click", this._titleClicked.bind(this), true);
+ element.addEventListener("click", this._titleClicked.bind(this), false);
var groupElement = element.enclosingNodeOrSelfWithClass("console-group");
if (groupElement && msg.type === WebInspector.ConsoleMessage.MessageType.StartGroupCollapsed)
groupElement.addStyleClass("collapsed");
diff --git a/WebCore/inspector/front-end/ElementsPanel.js b/WebCore/inspector/front-end/ElementsPanel.js
index c60d4b1..72b23e1 100644
--- a/WebCore/inspector/front-end/ElementsPanel.js
+++ b/WebCore/inspector/front-end/ElementsPanel.js
@@ -147,7 +147,7 @@ WebInspector.ElementsPanel.prototype = {
{
WebInspector.Panel.prototype.hide.call(this);
- WebInspector.hoveredDOMNode = null;
+ WebInspector.highlightDOMNode(0);
InspectorBackend.disableSearchingForNode();
},
@@ -165,7 +165,7 @@ WebInspector.ElementsPanel.prototype = {
this.rootDOMNode = null;
this.focusedDOMNode = null;
- WebInspector.hoveredDOMNode = null;
+ WebInspector.highlightDOMNode(0);
this.recentlyModifiedNodes = [];
@@ -187,9 +187,7 @@ WebInspector.ElementsPanel.prototype = {
inspectedRootDocument.addEventListener("DOMNodeRemoved", this._nodeRemoved.bind(this));
inspectedRootDocument.addEventListener("DOMAttrModified", this._attributesUpdated.bind(this));
- this.treeOutline.suppressSelectHighlight = true;
this.rootDOMNode = inspectedRootDocument;
- this.treeOutline.suppressSelectHighlight = false;
function selectNode(candidateFocusNode)
{
@@ -199,11 +197,9 @@ WebInspector.ElementsPanel.prototype = {
if (!candidateFocusNode)
return;
- this.treeOutline.suppressSelectHighlight = true;
this.focusedDOMNode = candidateFocusNode;
if (this.treeOutline.selectedTreeElement)
this.treeOutline.selectedTreeElement.expand();
- this.treeOutline.suppressSelectHighlight = false;
}
function selectLastSelectedNode(nodeId)
@@ -261,6 +257,23 @@ WebInspector.ElementsPanel.prototype = {
this._nodeSearchButton.toggled = false;
},
+ populateHrefContextMenu: function(contextMenu, event, anchorElement)
+ {
+ if (!anchorElement.href)
+ return false;
+
+ var resourceURL = WebInspector.resourceURLForRelatedNode(this.focusedDOMNode, anchorElement.href);
+ if (!resourceURL)
+ return false;
+
+ // Add resource-related actions.
+ // Keep these consistent with those added in WebInspector.StylesSidebarPane.prototype._populateHrefContextMenu().
+ contextMenu.appendItem(WebInspector.UIString("Open Link in New Window"), WebInspector.openResource.bind(null, resourceURL, false));
+ if (WebInspector.resourceForURL(resourceURL))
+ contextMenu.appendItem(WebInspector.UIString("Open Link in Resources Panel"), WebInspector.openResource.bind(null, resourceURL, true));
+ return true;
+ },
+
_updateMatchesCount: function()
{
WebInspector.updateSearchMatchesCount(this._searchResults.length, this);
@@ -561,7 +574,7 @@ WebInspector.ElementsPanel.prototype = {
var nodeUnderMouse = document.elementFromPoint(event.pageX, event.pageY);
var crumbElement = nodeUnderMouse.enclosingNodeOrSelfWithClass("crumb");
- WebInspector.hoveredDOMNode = (crumbElement ? crumbElement.representedObject : null);
+ WebInspector.highlightDOMNode(crumbElement ? crumbElement.representedObject.id : 0);
if ("_mouseOutOfCrumbsTimeout" in this) {
clearTimeout(this._mouseOutOfCrumbsTimeout);
@@ -575,7 +588,7 @@ WebInspector.ElementsPanel.prototype = {
if (nodeUnderMouse && nodeUnderMouse.isDescendant(this.crumbsElement))
return;
- WebInspector.hoveredDOMNode = null;
+ WebInspector.highlightDOMNode(0);
this._mouseOutOfCrumbsTimeout = setTimeout(this.updateBreadcrumbSizes.bind(this), 1000);
},
@@ -761,11 +774,19 @@ WebInspector.ElementsPanel.prototype = {
{
var link = document.createElement("span");
link.className = "node-link";
- link.addEventListener("click", WebInspector.updateFocusedNode.bind(WebInspector, node.id), false);
this.decorateNodeLabel(node, link);
+ WebInspector.wireElementWithDOMNode(link, node.id);
return link;
},
+ linkifyNodeById: function(nodeId)
+ {
+ var node = WebInspector.domAgent.nodeForId(nodeId);
+ if (!node)
+ return document.createTextNode(WebInspector.UIString("<node>"));
+ return this.linkifyNodeReference(node);
+ },
+
updateBreadcrumbSizes: function(focusedCrumb)
{
if (!this.visible)
diff --git a/WebCore/inspector/front-end/ElementsTreeOutline.js b/WebCore/inspector/front-end/ElementsTreeOutline.js
index 10131f4..e261234 100644
--- a/WebCore/inspector/front-end/ElementsTreeOutline.js
+++ b/WebCore/inspector/front-end/ElementsTreeOutline.js
@@ -93,27 +93,8 @@ WebInspector.ElementsTreeOutline.prototype = {
// and the select() call would change the focusedDOMNode and reenter this setter. So to
// avoid calling focusedNodeChanged() twice, first check if _focusedDOMNode is the same
// node as the one passed in.
- if (this._focusedDOMNode === x) {
+ if (this._focusedDOMNode === x)
this.focusedNodeChanged();
-
- if (x && !this.suppressSelectHighlight) {
- InspectorBackend.highlightDOMNode(x.id);
-
- if ("_restorePreviousHighlightNodeTimeout" in this)
- clearTimeout(this._restorePreviousHighlightNodeTimeout);
-
- function restoreHighlightToHoveredNode()
- {
- var hoveredNode = WebInspector.hoveredDOMNode;
- if (hoveredNode)
- InspectorBackend.highlightDOMNode(hoveredNode.id);
- else
- InspectorBackend.hideDOMNodeHighlight();
- }
-
- this._restorePreviousHighlightNodeTimeout = setTimeout(restoreHighlightToHoveredNode, 2000);
- }
- }
},
get editing()
@@ -260,7 +241,7 @@ WebInspector.ElementsTreeOutline.prototype = {
element._createTooltipForNode();
}
- WebInspector.hoveredDOMNode = (element ? element.representedObject : null);
+ WebInspector.highlightDOMNode(element ? element.representedObject.id : 0);
},
_onmouseout: function(event)
@@ -274,7 +255,7 @@ WebInspector.ElementsTreeOutline.prototype = {
delete this._previousHoveredElement;
}
- WebInspector.hoveredDOMNode = null;
+ WebInspector.highlightDOMNode(0);
},
_contextMenuEventFired: function(event)
@@ -285,12 +266,21 @@ WebInspector.ElementsTreeOutline.prototype = {
var contextMenu = new WebInspector.ContextMenu();
+ var href = event.target.enclosingNodeOrSelfWithClass("webkit-html-resource-link") || event.target.enclosingNodeOrSelfWithClass("webkit-html-external-link");
var tag = event.target.enclosingNodeOrSelfWithClass("webkit-html-tag");
var textNode = event.target.enclosingNodeOrSelfWithClass("webkit-html-text-node");
- if (tag && listItem.treeElement._populateTagContextMenu)
+ var needSeparator;
+ if (href)
+ needSeparator = WebInspector.panels.elements.populateHrefContextMenu(contextMenu, event, href);
+ if (tag && listItem.treeElement._populateTagContextMenu) {
+ if (needSeparator)
+ contextMenu.appendSeparator();
listItem.treeElement._populateTagContextMenu(contextMenu, event);
- else if (textNode && listItem.treeElement._populateTextContextMenu)
+ } else if (textNode && listItem.treeElement._populateTextContextMenu) {
+ if (needSeparator)
+ contextMenu.appendSeparator();
listItem.treeElement._populateTextContextMenu(contextMenu, textNode);
+ }
contextMenu.show(event);
}
}
@@ -1189,29 +1179,6 @@ WebInspector.ElementsTreeElement.prototype = {
this._highlightSearchResults();
},
- _rewriteAttrHref: function(node, hrefValue)
- {
- if (!hrefValue || hrefValue.indexOf("://") > 0)
- return hrefValue;
-
- for (var frameOwnerCandidate = node; frameOwnerCandidate; frameOwnerCandidate = frameOwnerCandidate.parentNode) {
- if (frameOwnerCandidate.documentURL) {
- var result = WebInspector.completeURL(frameOwnerCandidate.documentURL, hrefValue);
- if (result)
- return result;
- break;
- }
- }
-
- // documentURL not found or has bad value
- for (var url in WebInspector.resourceURLMap) {
- var match = url.match(WebInspector.URLRegExp);
- if (match && match[4] === hrefValue)
- return url;
- }
- return hrefValue;
- },
-
_attributeHTML: function(name, value, node, linkify)
{
var hasText = (value.length > 0);
@@ -1221,7 +1188,7 @@ WebInspector.ElementsTreeElement.prototype = {
html += "=&#8203;\"";
if (linkify && (name === "src" || name === "href")) {
- var rewrittenHref = this._rewriteAttrHref(node, value);
+ var rewrittenHref = WebInspector.resourceURLForRelatedNode(node, value);
value = value.replace(/([\/;:\)\]\}])/g, "$1\u200B");
html += linkify(rewrittenHref, value, "webkit-html-attribute-value", node.nodeName.toLowerCase() === "a");
} else {
diff --git a/WebCore/inspector/front-end/HAREntry.js b/WebCore/inspector/front-end/HAREntry.js
index 9f188ed..85e4f59 100644
--- a/WebCore/inspector/front-end/HAREntry.js
+++ b/WebCore/inspector/front-end/HAREntry.js
@@ -42,7 +42,7 @@ WebInspector.HAREntry.prototype = {
return {
pageref: this._resource.documentURL,
startedDateTime: new Date(this._resource.startTime * 1000),
- time: this._toMilliseconds(this._resource.duration),
+ time: WebInspector.HAREntry._toMilliseconds(this._resource.duration),
request: this._buildRequest(),
response: this._buildResponse(),
// cache: {...}, -- Not supproted yet.
@@ -95,14 +95,34 @@ WebInspector.HAREntry.prototype = {
_buildTimings: function()
{
+ var waitForConnection = this._interval("connectStart", "connectEnd");
+ var blocked;
+ var connect;
+ var dns = this._interval("dnsStart", "dnsEnd");
+ var send = this._interval("sendStart", "sendEnd");
+ var ssl = this._interval("sslStart", "sslEnd");
+
+ if (ssl !== -1 && send !== -1)
+ send -= ssl;
+
+ if (this._resource.connectionReused) {
+ connect = -1;
+ blocked = waitForConnection;
+ } else {
+ blocked = 0;
+ connect = waitForConnection;
+ if (dns !== -1)
+ connect -= dns;
+ }
+
return {
- blocked: -1, // Not available.
- dns: -1, // Not available.
- connect: -1, // Not available.
- send: -1, // Not available.
- wait: this._toMilliseconds(this._resource.latency),
- receive: this._toMilliseconds(this._resource.receiveDuration),
- ssl: -1 // Not available.
+ blocked: blocked,
+ dns: dns,
+ connect: connect,
+ send: send,
+ wait: this._interval("sendEnd", "receiveHeadersEnd"),
+ receive: WebInspector.HAREntry._toMilliseconds(this._resource.receiveDuration),
+ ssl: ssl
};
},
@@ -130,8 +150,65 @@ WebInspector.HAREntry.prototype = {
return parameters.slice();
},
- _toMilliseconds: function(time)
+ _interval: function(start, end)
+ {
+ var timing = this._resource.timing;
+ if (!timing)
+ return -1;
+ var startTime = timing[start];
+ return typeof startTime !== "number" || startTime === -1 ? -1 : Math.round(timing[end] - startTime);
+ }
+};
+
+WebInspector.HAREntry._toMilliseconds = function(time)
+{
+ return time === -1 ? -1 : Math.round(time * 1000);
+}
+
+WebInspector.HARLog = function()
+{
+}
+
+WebInspector.HARLog.prototype = {
+ build: function()
+ {
+ var webKitVersion = /AppleWebKit\/([^ ]+)/.exec(window.navigator.userAgent);
+
+ return {
+ version: "1.2",
+ creator: {
+ name: "WebInspector",
+ version: webKitVersion ? webKitVersion[1] : "n/a"
+ },
+ pages: this._buildPages(),
+ entries: Object.properties(WebInspector.resources).map(this._convertResource)
+ }
+ },
+
+ _buildPages: function()
+ {
+ return [
+ {
+ startedDateTime: new Date(WebInspector.mainResource.startTime * 1000),
+ id: WebInspector.mainResource.documentURL,
+ title: "",
+ pageTimings: this._buildMainResourceTimings()
+ }
+ ];
+ },
+
+ _buildMainResourceTimings: function()
+ {
+ var resourcesPanel = WebInspector.panels.resources;
+ var startTime = WebInspector.mainResource.startTime;
+ return {
+ onContentLoad: WebInspector.HAREntry._toMilliseconds(resourcesPanel.mainResourceDOMContentTime - startTime),
+ onLoad: WebInspector.HAREntry._toMilliseconds(resourcesPanel.mainResourceLoadTime - startTime),
+ }
+ },
+
+ _convertResource: function(id)
{
- return time === -1 ? -1 : Math.round(time * 1000);
+ return (new WebInspector.HAREntry(WebInspector.resources[id])).build();
}
};
diff --git a/WebCore/inspector/front-end/InjectedScript.js b/WebCore/inspector/front-end/InjectedScript.js
index d4e3d80..5544ed5 100644
--- a/WebCore/inspector/front-end/InjectedScript.js
+++ b/WebCore/inspector/front-end/InjectedScript.js
@@ -464,6 +464,10 @@ InjectedScript.prototype = {
return str.replace(/^\[object (.*)\]$/i, "$1");
} else {
// V8
+ if (isFinite(obj.length) && typeof obj.callee === "function") {
+ // Arguments.constructor === Object in V8
+ return "Arguments";
+ }
return obj.constructor && obj.constructor.name || "Object";
}
},
diff --git a/WebCore/inspector/front-end/ProfileDataGridTree.js b/WebCore/inspector/front-end/ProfileDataGridTree.js
index b10f392..adf34f1 100644
--- a/WebCore/inspector/front-end/ProfileDataGridTree.js
+++ b/WebCore/inspector/front-end/ProfileDataGridTree.js
@@ -96,19 +96,10 @@ WebInspector.ProfileDataGridNode.prototype = {
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;
- urlElement.preferredPanel = "scripts";
-
+ var lineNumber;
if (this.profileNode.lineNumber > 0)
- urlElement.textContent = fileName + ":" + this.profileNode.lineNumber;
- else
- urlElement.textContent = fileName;
-
+ lineNumber = this.profileNode.lineNumber;
+ var urlElement = WebInspector.linkifyResourceAsNode(this.profileNode.url, "scripts", lineNumber, "profile-node-file");
cell.insertBefore(urlElement, cell.firstChild);
}
diff --git a/WebCore/inspector/front-end/Resource.js b/WebCore/inspector/front-end/Resource.js
index de87047..ea9052d 100644
--- a/WebCore/inspector/front-end/Resource.js
+++ b/WebCore/inspector/front-end/Resource.js
@@ -45,7 +45,8 @@ WebInspector.Resource.Type = {
Script: 4,
XHR: 5,
Media: 6,
- Other: 7,
+ WebSocket: 7,
+ Other: 8,
isTextType: function(type)
{
@@ -76,6 +77,8 @@ WebInspector.Resource.Type = {
return "XHR";
case this.Media:
return "media";
+ case this.WebSocket:
+ return "WebSocket";
case this.Other:
default:
return "other";
@@ -372,6 +375,9 @@ WebInspector.Resource.prototype = {
case WebInspector.Resource.Type.XHR:
this.category = WebInspector.resourceCategories.xhr;
break;
+ case WebInspector.Resource.Type.WebSocket:
+ this.category = WebInspector.resourceCategories.websocket;
+ break;
case WebInspector.Resource.Type.Other:
default:
this.category = WebInspector.resourceCategories.other;
@@ -584,7 +590,8 @@ WebInspector.Resource.prototype = {
if (typeof this.type === "undefined"
|| this.type === WebInspector.Resource.Type.Other
- || this.type === WebInspector.Resource.Type.XHR)
+ || this.type === WebInspector.Resource.Type.XHR
+ || this.type === WebInspector.Resource.Type.WebSocket)
return true;
if (this.mimeType in WebInspector.MIMETypes)
diff --git a/WebCore/inspector/front-end/ResourceView.js b/WebCore/inspector/front-end/ResourceView.js
index 7ce09b6..1c2574f 100644
--- a/WebCore/inspector/front-end/ResourceView.js
+++ b/WebCore/inspector/front-end/ResourceView.js
@@ -279,13 +279,19 @@ WebInspector.ResourceView.prototype = {
_refreshRequestHeaders: function()
{
- this._refreshHeaders(WebInspector.UIString("Request Headers"), this.resource.sortedRequestHeaders, this.requestHeadersTreeElement);
+ var additionalRow = null;
+ if (typeof this.resource.webSocketRequestKey3 !== "undefined")
+ additionalRow = {header: "(Key3)", value: this.resource.webSocketRequestKey3};
+ this._refreshHeaders(WebInspector.UIString("Request Headers"), this.resource.sortedRequestHeaders, additionalRow, this.requestHeadersTreeElement);
this._refreshFormData();
},
_refreshResponseHeaders: function()
{
- this._refreshHeaders(WebInspector.UIString("Response Headers"), this.resource.sortedResponseHeaders, this.responseHeadersTreeElement);
+ var additionalRow = null;
+ if (typeof this.resource.webSocketChallengeResponse !== "undefined")
+ additionalRow = {header: "(Challenge Response)", value: this.resource.webSocketChallengeResponse};
+ this._refreshHeaders(WebInspector.UIString("Response Headers"), this.resource.sortedResponseHeaders, additionalRow, this.responseHeadersTreeElement);
},
_refreshHTTPInformation: function()
@@ -316,7 +322,7 @@ WebInspector.ResourceView.prototype = {
}
},
- _refreshHeaders: function(title, headers, headersTreeElement)
+ _refreshHeaders: function(title, headers, additionalRow, headersTreeElement)
{
headersTreeElement.removeChildren();
@@ -333,6 +339,15 @@ WebInspector.ResourceView.prototype = {
headerTreeElement.selectable = false;
headersTreeElement.appendChild(headerTreeElement);
}
+
+ if (additionalRow) {
+ var title = "<div class=\"header-name\">" + additionalRow.header.escapeHTML() + ":</div>";
+ title += "<div class=\"header-value source-code\">" + additionalRow.value.escapeHTML() + "</div>"
+
+ var headerTreeElement = new TreeElement(title, null, false);
+ headerTreeElement.selectable = false;
+ headersTreeElement.appendChild(headerTreeElement);
+ }
}
}
diff --git a/WebCore/inspector/front-end/ResourcesPanel.js b/WebCore/inspector/front-end/ResourcesPanel.js
index 27df5cf..f329b1a 100644
--- a/WebCore/inspector/front-end/ResourcesPanel.js
+++ b/WebCore/inspector/front-end/ResourcesPanel.js
@@ -47,6 +47,8 @@ WebInspector.ResourcesPanel = function()
this.filter(this.filterAllElement, false);
this.graphsTreeElement.children[0].select();
this._resourceTrackingEnabled = false;
+
+ this.sidebarElement.addEventListener("contextmenu", this._contextMenu.bind(this), true);
}
WebInspector.ResourcesPanel.prototype = {
@@ -751,16 +753,23 @@ WebInspector.ResourcesPanel.prototype = {
_toggleResourceTracking: function(optionalAlways)
{
+ function callback(newState) {
+ if (newState)
+ WebInspector.panels.resources.resourceTrackingWasEnabled();
+ else
+ WebInspector.panels.resources.resourceTrackingWasDisabled();
+ }
+
if (this._resourceTrackingEnabled) {
this.largerResourcesButton.visible = false;
this.sortingSelectElement.visible = false;
WebInspector.resources = {};
WebInspector.resourceURLMap = {};
- InspectorBackend.disableResourceTracking(true);
+ InspectorBackend.setResourceTrackingEnabled(false, true, callback);
} else {
this.largerResourcesButton.visible = true;
this.sortingSelectElement.visible = true;
- InspectorBackend.enableResourceTracking(!!optionalAlways);
+ InspectorBackend.setResourceTrackingEnabled(true, !!optionalAlways, callback);
}
},
@@ -857,7 +866,7 @@ WebInspector.ResourcesPanel.prototype = {
var title = document.createElement("span");
title.className = "resource-timing-bar-title";
- if (i >= rows.length - 2)
+ if (total - rows[i].end < rows[i].start)
title.style.right = (scale * (total - rows[i].end) + 3) + "px";
else
title.style.left = (scale * rows[i].start + 3) + "px";
@@ -876,6 +885,36 @@ WebInspector.ResourcesPanel.prototype = {
{
WebInspector.Panel.prototype.hide.call(this);
this._popoverHelper.hidePopup();
+ },
+
+ _contextMenu: function(event)
+ {
+ // createBlobURL is enabled conditionally, do not expose resource export if it's not available.
+ if (typeof window.createBlobURL !== "function" || !Preferences.resourceExportEnabled)
+ return;
+
+ var contextMenu = new WebInspector.ContextMenu();
+ var resourceTreeItem = event.target.enclosingNodeOrSelfWithClass("resource-sidebar-tree-item");
+ if (resourceTreeItem && resourceTreeItem.treeElement) {
+ var resource = resourceTreeItem.treeElement.representedObject;
+ contextMenu.appendItem(WebInspector.UIString("Export to HAR"), this._exportResource.bind(this, resource));
+ }
+ contextMenu.appendItem(WebInspector.UIString("Export all to HAR"), this._exportAll.bind(this));
+ contextMenu.show(event);
+ },
+
+ _exportAll: function()
+ {
+ var harArchive = {
+ log: (new WebInspector.HARLog()).build()
+ }
+ offerFileForDownload(JSON.stringify(harArchive));
+ },
+
+ _exportResource: function(resource)
+ {
+ var har = (new WebInspector.HAREntry(resource)).build();
+ offerFileForDownload(JSON.stringify(har));
}
}
diff --git a/WebCore/inspector/front-end/ScriptsPanel.js b/WebCore/inspector/front-end/ScriptsPanel.js
index c5267f7..715339d 100644
--- a/WebCore/inspector/front-end/ScriptsPanel.js
+++ b/WebCore/inspector/front-end/ScriptsPanel.js
@@ -375,7 +375,7 @@ WebInspector.ScriptsPanel.prototype = {
InjectedScriptAccess.get(callFrame.worldId).evaluateInCallFrame(callFrame.id, code, objectGroup, evalCallback);
},
- debuggerPaused: function(callFrames)
+ debuggerPaused: function(details)
{
WebInspector.breakpointManager.removeOneTimeBreakpoint();
this._paused = true;
@@ -384,8 +384,11 @@ WebInspector.ScriptsPanel.prototype = {
this._updateDebuggerButtons();
- this.sidebarPanes.callstack.update(callFrames, this._sourceIDMap);
- this.sidebarPanes.callstack.selectedCallFrame = callFrames[0];
+ this.sidebarPanes.callstack.update(details.callFrames, this._sourceIDMap);
+ this.sidebarPanes.callstack.selectedCallFrame = details.callFrames[0];
+
+ if (details.status)
+ this.sidebarPanes.callstack.updateStatus(details.status);
WebInspector.currentPanel = this;
window.focus();
diff --git a/WebCore/inspector/front-end/Settings.js b/WebCore/inspector/front-end/Settings.js
index 41d82f9..63f2641 100644
--- a/WebCore/inspector/front-end/Settings.js
+++ b/WebCore/inspector/front-end/Settings.js
@@ -44,7 +44,8 @@ var Preferences = {
profilerAlwaysEnabled: false,
auditsPanelEnabled: true,
onlineDetectionEnabled: true,
- domBreakpointsEnabled: false
+ domBreakpointsEnabled: false,
+ resourceExportEnabled: false
}
WebInspector.Settings = function(sessionScope)
diff --git a/WebCore/inspector/front-end/StylesSidebarPane.js b/WebCore/inspector/front-end/StylesSidebarPane.js
index 1dddde7..6aff37d 100644
--- a/WebCore/inspector/front-end/StylesSidebarPane.js
+++ b/WebCore/inspector/front-end/StylesSidebarPane.js
@@ -64,6 +64,7 @@ WebInspector.StylesSidebarPane = function(computedStylePane)
this.titleElement.appendChild(this.settingsSelectElement);
this._computedStylePane = computedStylePane;
+ this.element.addEventListener("contextmenu", this._contextMenuEventFired.bind(this), true);
}
// Taken from http://www.w3.org/TR/CSS21/propidx.html.
@@ -105,6 +106,17 @@ WebInspector.StylesSidebarPane.prototype = {
this.settingsSelectElement[2].selected = true;
},
+ _contextMenuEventFired: function(event)
+ {
+ var href = event.target.enclosingNodeOrSelfWithClass("webkit-html-resource-link") || event.target.enclosingNodeOrSelfWithClass("webkit-html-external-link");
+ if (href) {
+ var contextMenu = new WebInspector.ContextMenu();
+ var filled = WebInspector.panels.elements.populateHrefContextMenu(contextMenu, event, href);
+ if (filled)
+ contextMenu.show(event);
+ }
+ },
+
update: function(node, editedSection, forceUpdate)
{
var refresh = false;
diff --git a/WebCore/inspector/front-end/WebKit.qrc b/WebCore/inspector/front-end/WebKit.qrc
index f90a9fe..c222f0e 100644
--- a/WebCore/inspector/front-end/WebKit.qrc
+++ b/WebCore/inspector/front-end/WebKit.qrc
@@ -266,5 +266,6 @@
<file>Images/warningOrangeDot.png</file>
<file>Images/warningsErrors.png</file>
<file>Images/whiteConnectorPoint.png</file>
+ <file alias="DebuggerScript.js">../../bindings/v8/DebuggerScript.js</file>
</qresource>
</RCC>
diff --git a/WebCore/inspector/front-end/inspector.css b/WebCore/inspector/front-end/inspector.css
index 4319816..6d8571c 100644
--- a/WebCore/inspector/front-end/inspector.css
+++ b/WebCore/inspector/front-end/inspector.css
@@ -1693,6 +1693,10 @@ li.editing .swatch, li.editing .enabled-button, li.editing-sub-part .delete-but
color: gray;
}
+.pane > .body .placard + .info {
+ border-top: 1px solid gray
+}
+
.pane.expanded > .body, .pane.expanded > .growbar {
display: block;
}
@@ -2835,7 +2839,8 @@ button.enable-toggle-status-bar-item.toggled-on .glyph {
}
.resources-category-documents, .resources-category-stylesheets, .resources-category-images,
-.resources-category-scripts, .resources-category-xhr, .resources-category-fonts, .resources-category-other {
+.resources-category-scripts, .resources-category-xhr, .resources-category-fonts,
+.resources-category-websockets, .resources-category-other {
display: none;
}
@@ -2845,6 +2850,7 @@ button.enable-toggle-status-bar-item.toggled-on .glyph {
.filter-all .resources-category-scripts, .filter-scripts .resources-category-scripts,
.filter-all .resources-category-xhr, .filter-xhr .resources-category-xhr,
.filter-all .resources-category-fonts, .filter-fonts .resources-category-fonts,
+.filter-all .resources-category-websockets, .filter-websockets .resources-category-websockets,
.filter-all .resources-category-other, .filter-other .resources-category-other,
.resource-sidebar-tree-item.selected {
display: list-item;
@@ -2920,6 +2926,15 @@ button.enable-toggle-status-bar-item.toggled-on .glyph {
-webkit-border-image: url(Images/timelineHollowPillYellow.png) 6 7 6 7;
}
+/* FIXME: Create bar images for WebSocket. */
+.resources-category-websockets .resources-graph-bar {
+ -webkit-border-image: url(Images/timelinePillGray.png) 6 7 6 7;
+}
+
+.resources-category-websockets.resource-cached .resources-graph-bar {
+ -webkit-border-image: url(Images/timelineHollowPillGray.png) 6 7 6 7;
+}
+
#resource-views {
position: absolute;
top: 23px;
diff --git a/WebCore/inspector/front-end/inspector.js b/WebCore/inspector/front-end/inspector.js
index 840745f..f6fa06b 100644
--- a/WebCore/inspector/front-end/inspector.js
+++ b/WebCore/inspector/front-end/inspector.js
@@ -403,45 +403,44 @@ var WebInspector = {
}
},
- get hoveredDOMNode()
+ highlightDOMNode: function(nodeId)
{
- return this._hoveredDOMNode;
- },
+ if ("_hideDOMNodeHighlightTimeout" in this) {
+ clearTimeout(this._hideDOMNodeHighlightTimeout);
+ delete this._hideDOMNodeHighlightTimeout;
+ }
- set hoveredDOMNode(x)
- {
- if (this._hoveredDOMNode === x)
+ if (this._highlightedDOMNodeId === nodeId)
return;
- this._hoveredDOMNode = x;
-
- if (this._hoveredDOMNode)
- this._updateHoverHighlightSoon(this.showingDOMNodeHighlight ? 50 : 500);
+ this._highlightedDOMNodeId = nodeId;
+ if (nodeId)
+ InspectorBackend.highlightDOMNode(nodeId);
else
- this._updateHoverHighlight();
+ InspectorBackend.hideDOMNodeHighlight();
},
- _updateHoverHighlightSoon: function(delay)
+ highlightDOMNodeForTwoSeconds: function(nodeId)
{
- if ("_updateHoverHighlightTimeout" in this)
- clearTimeout(this._updateHoverHighlightTimeout);
- this._updateHoverHighlightTimeout = setTimeout(this._updateHoverHighlight.bind(this), delay);
+ this.highlightDOMNode(nodeId);
+ this._hideDOMNodeHighlightTimeout = setTimeout(this.highlightDOMNode.bind(this, 0), 2000);
},
- _updateHoverHighlight: function()
+ wireElementWithDOMNode: function(element, nodeId)
{
- if ("_updateHoverHighlightTimeout" in this) {
- clearTimeout(this._updateHoverHighlightTimeout);
- delete this._updateHoverHighlightTimeout;
- }
+ element.addEventListener("click", this._updateFocusedNode.bind(this, nodeId), false);
+ element.addEventListener("mouseover", this.highlightDOMNode.bind(this, nodeId), false);
+ element.addEventListener("mouseout", this.highlightDOMNode.bind(this, 0), false);
+ },
- if (this._hoveredDOMNode) {
- InspectorBackend.highlightDOMNode(this._hoveredDOMNode.id);
- this.showingDOMNodeHighlight = true;
- } else {
- InspectorBackend.hideDOMNodeHighlight();
- this.showingDOMNodeHighlight = false;
- }
+ _updateFocusedNode: function(nodeId)
+ {
+ var node = WebInspector.domAgent.nodeForId(nodeId);
+ if (!node)
+ return;
+
+ this.currentPanel = this.panels.elements;
+ this.panels.elements.focusedDOMNode = node;
}
}
@@ -517,6 +516,7 @@ WebInspector.doLoadedDone = function()
scripts: new WebInspector.ResourceCategory("scripts", WebInspector.UIString("Scripts"), "rgb(255,121,0)"),
xhr: new WebInspector.ResourceCategory("xhr", WebInspector.UIString("XHR"), "rgb(231,231,10)"),
fonts: new WebInspector.ResourceCategory("fonts", WebInspector.UIString("Fonts"), "rgb(255,82,62)"),
+ websocket: new WebInspector.ResourceCategory("websockets", WebInspector.UIString("WebSocket"), "rgb(186,186,186)"), // FIXME: Decide the color.
other: new WebInspector.ResourceCategory("other", WebInspector.UIString("Other"), "rgb(186,186,186)")
};
@@ -582,6 +582,16 @@ WebInspector.doLoadedDone = function()
this.extensionServer.initExtensions();
+ function populateInspectorState(inspectorState)
+ {
+ WebInspector.monitoringXHREnabled = inspectorState.monitoringXHREnabled;
+ if (inspectorState.resourceTrackingEnabled)
+ WebInspector.panels.resources.resourceTrackingWasEnabled();
+ else
+ WebInspector.panels.resources.resourceTrackingWasDisabled();
+ }
+ InspectorBackend.getInspectorState(populateInspectorState);
+
InspectorBackend.populateScriptObjects();
// As a DOMAgent method, this needs to happen after the frontend has loaded and the agent is available.
@@ -720,22 +730,10 @@ WebInspector.disconnectFromBackend = function()
InspectorFrontendHost.disconnectFromBackend();
}
-WebInspector.documentMouseOver = function(event)
-{
- if (event.target.tagName !== "A")
- return;
-
- const anchor = event.target;
- if (!anchor.hasStyleClass("webkit-html-resource-link"))
- return;
- if (anchor.href && anchor.href.indexOf("/data:") != -1)
- return;
-}
-
WebInspector.documentClick = function(event)
{
var anchor = event.target.enclosingNodeOrSelfWithNodeName("a");
- if (!anchor)
+ if (!anchor || anchor.target === "_blank")
return;
// Prevent the link from navigating, since we don't do any navigation by following links normally.
@@ -788,6 +786,16 @@ WebInspector.documentClick = function(event)
followLink();
}
+WebInspector.openResource = function(resourceURL, inResourcesPanel)
+{
+ var resource = WebInspector.resourceForURL(resourceURL);
+ if (inResourcesPanel && resource) {
+ WebInspector.panels.resources.showResource(resource);
+ WebInspector.showPanel("resources");
+ } else
+ InspectorBackend.openInInspectedWindow(resource ? resource.url : resourceURL);
+}
+
WebInspector._registerShortcuts = function()
{
var shortcut = WebInspector.KeyboardShortcut;
@@ -1223,6 +1231,8 @@ WebInspector.updateResource = function(payload)
resource.requestMethod = payload.requestMethod;
resource.requestFormData = payload.requestFormData;
resource.documentURL = payload.documentURL;
+ if (typeof payload.webSocketRequestKey3 !== "undefined")
+ resource.webSocketRequestKey3 = payload.webSocketRequestKey3;
if (resource.mainResource)
this.mainResource = resource;
@@ -1247,6 +1257,8 @@ WebInspector.updateResource = function(payload)
resource.connectionReused = payload.connectionReused;
resource.timing = payload.timing;
resource.cached = payload.cached;
+ if (typeof payload.webSocketChallengeResponse !== "undefined")
+ resource.webSocketChallengeResponse = payload.webSocketChallengeResponse;
}
if (payload.didTypeChange) {
@@ -1370,16 +1382,6 @@ WebInspector.updateNetworkState = function(isNowOnline)
this.panels.storage.updateNetworkState(isNowOnline);
}
-WebInspector.resourceTrackingWasEnabled = function()
-{
- this.panels.resources.resourceTrackingWasEnabled();
-}
-
-WebInspector.resourceTrackingWasDisabled = function()
-{
- this.panels.resources.resourceTrackingWasDisabled();
-}
-
WebInspector.searchingForNodeWasEnabled = function()
{
this.panels.elements.searchingForNodeWasEnabled();
@@ -1390,16 +1392,6 @@ WebInspector.searchingForNodeWasDisabled = function()
this.panels.elements.searchingForNodeWasDisabled();
}
-WebInspector.monitoringXHRWasEnabled = function()
-{
- this.monitoringXHREnabled = true;
-}
-
-WebInspector.monitoringXHRWasDisabled = function()
-{
- this.monitoringXHREnabled = false;
-}
-
WebInspector.attachDebuggerWhenShown = function()
{
this.panels.scripts.attachDebuggerWhenShown();
@@ -1447,7 +1439,7 @@ WebInspector.failedToParseScriptSource = function(sourceURL, source, startingLin
WebInspector.pausedScript = function(details)
{
- this.panels.scripts.debuggerPaused(details.callFrames);
+ this.panels.scripts.debuggerPaused(details);
InspectorFrontendHost.bringToFront();
}
@@ -1474,7 +1466,7 @@ WebInspector.reset = function()
this.resourceURLMap = {};
this.cookieDomains = {};
this.applicationCacheDomains = {};
- this.hoveredDOMNode = null;
+ this.highlightDOMNode(0);
delete this.mainResource;
@@ -1683,13 +1675,8 @@ WebInspector.drawLoadingPieChart = function(canvas, percent) {
WebInspector.updateFocusedNode = function(nodeId)
{
- var node = WebInspector.domAgent.nodeForId(nodeId);
- if (!node)
- // FIXME: Should we deselect if null is passed in?
- return;
-
- this.currentPanel = this.panels.elements;
- this.panels.elements.focusedDOMNode = node;
+ this._updateFocusedNode(nodeId);
+ this.highlightDOMNodeForTwoSeconds(nodeId);
}
WebInspector.displayNameForURL = function(url)
@@ -1804,7 +1791,6 @@ WebInspector.linkifyURLAsNode = function(url, linkText, classes, isExternal, too
a.title = url;
else if (typeof tooltipText !== "string" || tooltipText.length)
a.title = tooltipText;
- a.target = "_blank";
a.textContent = linkText;
return a;
@@ -1828,6 +1814,29 @@ WebInspector.linkifyResourceAsNode = function(url, preferredPanel, lineNumber, c
return node;
}
+WebInspector.resourceURLForRelatedNode = function(node, url)
+{
+ if (!url || url.indexOf("://") > 0)
+ return url;
+
+ for (var frameOwnerCandidate = node; frameOwnerCandidate; frameOwnerCandidate = frameOwnerCandidate.parentNode) {
+ if (frameOwnerCandidate.documentURL) {
+ var result = WebInspector.completeURL(frameOwnerCandidate.documentURL, url);
+ if (result)
+ return result;
+ break;
+ }
+ }
+
+ // documentURL not found or has bad value
+ for (var resourceURL in WebInspector.resourceURLMap) {
+ var match = resourceURL.match(WebInspector.URLRegExp);
+ if (match && match[4] === url)
+ return resourceURL;
+ }
+ return url;
+},
+
WebInspector.completeURL = function(baseURL, href)
{
var match = baseURL.match(WebInspector.URLRegExp);
@@ -1850,7 +1859,6 @@ WebInspector.addMainEventListeners = function(doc)
doc.defaultView.addEventListener("focus", this.windowFocused.bind(this), false);
doc.defaultView.addEventListener("blur", this.windowBlurred.bind(this), false);
doc.addEventListener("click", this.documentClick.bind(this), true);
- doc.addEventListener("mouseover", this.documentMouseOver.bind(this), true);
}
WebInspector._searchFieldManualFocus = function(event)
@@ -2008,6 +2016,11 @@ WebInspector.UIString = function(string)
return String.vsprintf(string, Array.prototype.slice.call(arguments, 1));
}
+WebInspector.formatLocalized = function(format, substitutions, formatters, initialValue, append)
+{
+ return String.format(WebInspector.UIString(format), substitutions, formatters, initialValue, append);
+}
+
WebInspector.isMac = function()
{
if (!("_isMac" in this))
diff --git a/WebCore/inspector/front-end/treeoutline.js b/WebCore/inspector/front-end/treeoutline.js
index 5891401..c2a3fe7 100644
--- a/WebCore/inspector/front-end/treeoutline.js
+++ b/WebCore/inspector/front-end/treeoutline.js
@@ -651,6 +651,7 @@ TreeElement.treeElementToggled = function(event)
else
element.treeElement.expand();
}
+ event.stopPropagation();
}
TreeElement.treeElementDoubleClicked = function(event)
diff --git a/WebCore/inspector/front-end/utilities.js b/WebCore/inspector/front-end/utilities.js
index e8adff6..5e41da6 100644
--- a/WebCore/inspector/front-end/utilities.js
+++ b/WebCore/inspector/front-end/utilities.js
@@ -984,3 +984,12 @@ function createSearchRegex(query)
}
return new RegExp(regex, "i");
}
+
+function offerFileForDownload(contents)
+{
+ var builder = new BlobBuilder();
+ builder.append(contents);
+ var blob = builder.getBlob("application/octet-stream");
+ var url = window.createBlobURL(blob);
+ window.open(url);
+}