Title: [193956] trunk
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));
     }
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to