- Revision
- 193956
- Author
- nvasil...@apple.com
- Date
- 2015-12-11 09:34:32 -0800 (Fri, 11 Dec 2015)
Log Message
Web Inspector: When logging strings, detect stack traces and show them as StackTraceView
https://bugs.webkit.org/show_bug.cgi?id=149790
Reviewed by Timothy Hatcher.
Source/WebInspectorUI:
* UserInterface/Models/StackTrace.js:
(WebInspector.StackTrace.isLikelyStackTrace): Added.
* UserInterface/Views/ConsoleMessageView.css:
(.console-message-extra-parameter .stack-trace):
Display stack trace view on the same line as a list bullet point from
console message extra parameter.
* UserInterface/Views/ConsoleMessageView.js:
(WebInspector.ConsoleMessageView.prototype._appendFormattedArguments):
Don't format with string substitutions for stack traces. E.g. there is
no need to replace %s with the next argument.
(WebInspector.ConsoleMessageView.prototype._isStackTrace): Added.
(WebInspector.ConsoleMessageView.prototype._formatParameterAsString):
Detect stack traces and format them appropriately.
LayoutTests:
* inspector/console/js-isLikelyStackTrace.html: Added.
Modified Paths
Added Paths
Diff
Modified: trunk/LayoutTests/ChangeLog (193955 => 193956)
--- trunk/LayoutTests/ChangeLog 2015-12-11 17:15:58 UTC (rev 193955)
+++ trunk/LayoutTests/ChangeLog 2015-12-11 17:34:32 UTC (rev 193956)
@@ -1,3 +1,12 @@
+2015-12-11 Nikita Vasilyev <nvasil...@apple.com>
+
+ Web Inspector: When logging strings, detect stack traces and show them as StackTraceView
+ https://bugs.webkit.org/show_bug.cgi?id=149790
+
+ Reviewed by Timothy Hatcher.
+
+ * inspector/console/js-isLikelyStackTrace.html: Added.
+
2015-12-10 Brady Eidson <beid...@apple.com>
Modern IDB: storage/indexeddb/objectstore-count.html fails.
Added: trunk/LayoutTests/inspector/console/js-isLikelyStackTrace-expected.txt (0 => 193956)
--- trunk/LayoutTests/inspector/console/js-isLikelyStackTrace-expected.txt (rev 0)
+++ trunk/LayoutTests/inspector/console/js-isLikelyStackTrace-expected.txt 2015-12-11 17:34:32 UTC (rev 193956)
@@ -0,0 +1,15 @@
+Test stack trace detection heuristic.
+
+== Running test suite: WebInspector.StackTrace.isLikelyStackTrace
+-- Running test case: notStacktrace
+ASSERT: Should NOT be a stacktrace.
+
+-- Running test case: typeErrorWrap
+ASSERT: Should be a stacktrace.
+
+-- Running test case: getAnonymousFunctionError
+ASSERT: Should be a stacktrace.
+
+-- Running test case: testWithNativeCallInBetween
+ASSERT: Should be a stacktrace.
+
Added: trunk/LayoutTests/inspector/console/js-isLikelyStackTrace.html (0 => 193956)
--- trunk/LayoutTests/inspector/console/js-isLikelyStackTrace.html (rev 0)
+++ trunk/LayoutTests/inspector/console/js-isLikelyStackTrace.html 2015-12-11 17:34:32 UTC (rev 193956)
@@ -0,0 +1,106 @@
+<html>
+<head>
+<script src=""
+<script>
+function typeErrorWrap()
+{
+ return typeError();
+}
+
+function typeError()
+{
+ var object = {};
+ try {
+ object.propertyDoesnt.exist;
+ } catch(e) {
+ console.trace();
+ return e.stack;
+ }
+}
+
+function testWithNativeCallInBetween()
+{
+ return [42].map(typeError)[0];
+}
+
+var _anonymousFunctionError = null;
+
+(function() {
+ var object = {};
+ try {
+ object.methodDoesntExist();
+ } catch(e) {
+ _anonymousFunctionError = e.stack;
+ }
+})();
+
+function getAnonymousFunctionError()
+{
+ return _anonymousFunctionError;
+}
+
+function test()
+{
+ let suite = InspectorTest.createAsyncSuite("WebInspector.StackTrace.isLikelyStackTrace");
+
+ suite.addTestCase({
+ name: "notStacktrace",
+ test: function(resolve, reject) {
+ var result = WebInspector.StackTrace.isLikelyStackTrace("Messages:42\nUnread:16");
+ InspectorTest.assert(result, "Should NOT be a stacktrace.");
+ resolve();
+ }
+ });
+
+ suite.addTestCase({
+ name: "typeErrorWrap",
+ test: function(resolve, reject) {
+ InspectorTest.evaluateInPage("typeErrorWrap()", function(error, result) {
+ if (error)
+ reject(error);
+
+ InspectorTest.assert(WebInspector.StackTrace.isLikelyStackTrace(result), "Should be a stacktrace.");
+
+ resolve();
+ });
+ }
+ });
+
+ suite.addTestCase({
+ name: "getAnonymousFunctionError",
+ test: function(resolve, reject) {
+ InspectorTest.evaluateInPage("getAnonymousFunctionError()", function(error, result) {
+ if (error)
+ reject(error);
+
+ InspectorTest.assert(WebInspector.StackTrace.isLikelyStackTrace(result), "Should be a stacktrace.");
+
+ resolve();
+ });
+ }
+ });
+
+ suite.addTestCase({
+ name: "testWithNativeCallInBetween",
+ test: function(resolve, reject) {
+ InspectorTest.evaluateInPage("testWithNativeCallInBetween()", function(error, result) {
+ if (error)
+ reject(error);
+
+ InspectorTest.assert(WebInspector.StackTrace.isLikelyStackTrace(result), "Should be a stacktrace.");
+
+ resolve();
+ });
+ }
+ });
+
+ suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body _onload_="runTest()">
+<p>
+Test stack trace detection heuristic.<br>
+</p>
+</body>
+</html>
Modified: trunk/Source/WebInspectorUI/ChangeLog (193955 => 193956)
--- trunk/Source/WebInspectorUI/ChangeLog 2015-12-11 17:15:58 UTC (rev 193955)
+++ trunk/Source/WebInspectorUI/ChangeLog 2015-12-11 17:34:32 UTC (rev 193956)
@@ -1,3 +1,27 @@
+2015-12-11 Nikita Vasilyev <nvasil...@apple.com>
+
+ Web Inspector: When logging strings, detect stack traces and show them as StackTraceView
+ https://bugs.webkit.org/show_bug.cgi?id=149790
+
+ Reviewed by Timothy Hatcher.
+
+ * UserInterface/Models/StackTrace.js:
+ (WebInspector.StackTrace.isLikelyStackTrace): Added.
+
+ * UserInterface/Views/ConsoleMessageView.css:
+ (.console-message-extra-parameter .stack-trace):
+ Display stack trace view on the same line as a list bullet point from
+ console message extra parameter.
+
+ * UserInterface/Views/ConsoleMessageView.js:
+ (WebInspector.ConsoleMessageView.prototype._appendFormattedArguments):
+ Don't format with string substitutions for stack traces. E.g. there is
+ no need to replace %s with the next argument.
+
+ (WebInspector.ConsoleMessageView.prototype._isStackTrace): Added.
+ (WebInspector.ConsoleMessageView.prototype._formatParameterAsString):
+ Detect stack traces and format them appropriately.
+
2015-12-10 Nikita Vasilyev <nvasil...@apple.com>
Web Inspector: debugger dashboard's switching arrows are positioned too close to the dashboard border
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/StackTrace.js (193955 => 193956)
--- trunk/Source/WebInspectorUI/UserInterface/Models/StackTrace.js 2015-12-11 17:15:58 UTC (rev 193955)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/StackTrace.js 2015-12-11 17:34:32 UTC (rev 193956)
@@ -48,6 +48,31 @@
return WebInspector.StackTrace.fromPayload(payload);
}
+ // May produce false negatives; must not produce any false positives.
+ // It may return false on a valid stack trace, but it will never return true on an invalid stack trace.
+ static isLikelyStackTrace(stack)
+ {
+ // This function runs for every logged string. It penalizes the performance.
+ // As most logged strings are not stack traces, exit as early as possible.
+ const smallestPossibleStackTraceLength = "http://a.bc/:9:1".length;
+ if (stack.length < smallestPossibleStackTraceLength.length * 2)
+ return false;
+
+ const approximateStackLengthOf50Items = 5000;
+ if (stack.length > approximateStackLengthOf50Items)
+ return false;
+
+ if (/^[^a-z$_]/i.test(stack[0]))
+ return false;
+
+ const reasonablyLongLineLength = 500;
+ const reasonablyLongNativeMethodLength = 120;
+ const stackTraceLine = `(.{1,${reasonablyLongLineLength}}:\\d+:\\d+|eval code|.{1,${reasonablyLongNativeMethodLength}}@\\[native code\\])`;
+ const stackTrace = new RegExp(`^${stackTraceLine}(\\n${stackTraceLine})*$`, "g");
+
+ return stackTrace.test(stack);
+ }
+
static _parseStackTrace(stack)
{
var lines = stack.split(/\n/g);
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ConsoleMessageView.css (193955 => 193956)
--- trunk/Source/WebInspectorUI/UserInterface/Views/ConsoleMessageView.css 2015-12-11 17:15:58 UTC (rev 193955)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ConsoleMessageView.css 2015-12-11 17:34:32 UTC (rev 193956)
@@ -237,6 +237,10 @@
-webkit-user-select: text;
}
+.console-message-extra-parameter .stack-trace {
+ display: inline-block;
+}
+
.console-message-location.call-frame {
display: inline-block;
-webkit-user-select: text;
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ConsoleMessageView.js (193955 => 193956)
--- trunk/Source/WebInspectorUI/UserInterface/Views/ConsoleMessageView.js 2015-12-11 17:15:58 UTC (rev 193955)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ConsoleMessageView.js 2015-12-11 17:34:32 UTC (rev 193956)
@@ -419,6 +419,9 @@
console.assert(this._message.type !== WebInspector.ConsoleMessage.MessageType.Result);
+ if (shouldFormatWithStringSubstitution && this._isStackTrace(parameters[0]))
+ shouldFormatWithStringSubstitution = false;
+
// Format string / message / default message.
if (shouldFormatWithStringSubstitution) {
var result = this._formatWithSubstitutionString(parameters, builderElement);
@@ -431,16 +434,19 @@
// Trailing parameters.
if (parameters.length) {
- if (parameters.length === 1) {
+ let enclosedElement = document.createElement("span");
+
+ if (parameters.length === 1 && !this._isStackTrace(parameters[0])) {
+ let parameter = parameters[0];
+
// Single object. Show a preview.
- var enclosedElement = builderElement.appendChild(document.createElement("span"));
+ builderElement.append(enclosedElement);
enclosedElement.classList.add("console-message-preview-divider");
enclosedElement.textContent = " \u2013 ";
var previewContainer = builderElement.appendChild(document.createElement("span"));
previewContainer.classList.add("console-message-preview");
- var parameter = parameters[0];
var preview = WebInspector.FormattedValue.createObjectPreviewOrFormattedValueForRemoteObject(parameter, WebInspector.ObjectPreviewView.Mode.Brief);
var isPreviewView = preview instanceof WebInspector.ObjectPreviewView;
@@ -455,14 +461,23 @@
this._extraParameters = null;
} else {
// Multiple objects. Show an indicator.
- builderElement.append(" ");
- var enclosedElement = builderElement.appendChild(document.createElement("span"));
+ builderElement.append(" ", enclosedElement);
enclosedElement.classList.add("console-message-enclosed");
enclosedElement.textContent = "(" + parameters.length + ")";
}
}
}
+ _isStackTrace(parameter)
+ {
+ console.assert(parameter instanceof WebInspector.RemoteObject);
+
+ if (WebInspector.RemoteObject.type(parameter) !== "string")
+ return false;
+
+ return WebInspector.StackTrace.isLikelyStackTrace(parameter.description);
+ }
+
_shouldConsiderObjectLossless(object)
{
if (object.type === "string") {
@@ -515,6 +530,15 @@
_formatParameterAsString(object, fragment)
{
+ if (this._isStackTrace(object)) {
+ let stackTrace = WebInspector.StackTrace.fromString(object.description);
+ if (stackTrace.callFrames.length) {
+ let stackView = new WebInspector.StackTraceView(stackTrace);
+ fragment.appendChild(stackView.element);
+ return;
+ }
+ }
+
fragment.appendChild(WebInspector.FormattedValue.createLinkifiedElementString(object.description));
}