Changeset 272371 in webkit


Ignore:
Timestamp:
Feb 4, 2021 9:16:03 AM (18 months ago)
Author:
Nikita Vasilyev
Message:

Web Inspector: Collapse blackboxed call frames in Sources
https://bugs.webkit.org/show_bug.cgi?id=216897

Reviewed by Devin Rousso.

Source/WebInspectorUI:

Stack traces often have dozens of blackboxed call frames when using blackboxing for JS-frameworks such as React.js.
It makes it hard to see more relevant non-framework code.

To improve this, introduce "Collapse blackboxed call frames" experimental setting, that collapses adjacent
blackboxed call frames into expandable items.

  • Localizations/en.lproj/localizedStrings.js:
  • UserInterface/Base/Setting.js:
  • UserInterface/Base/Utilities.js:
  • UserInterface/Images/TypeIcons.svg:
  • UserInterface/Main.html:
  • UserInterface/Models/CallFrame.js:

(WI.CallFrame):
(WI.CallFrame.prototype.get blackboxed):
(WI.CallFrame.fromDebuggerPayload):
(WI.CallFrame.fromPayload):

  • UserInterface/Views/BlackboxedGroupTreeElement.css: Added.

(.tree-outline .item.blackboxed-group):
(.tree-outline .item.blackboxed-group .icon):
(@media (prefers-color-scheme: dark) .tree-outline .item.blackboxed-group .icon):

  • UserInterface/Views/BlackboxedGroupTreeElement.js: Added.

(WI.BlackboxedGroupTreeElement):
(WI.BlackboxedGroupTreeElement.prototype.expand):

  • UserInterface/Views/CallFrameTreeElement.css:

(.tree-outline .item.call-frame.blackboxed:not(.selected)):

  • UserInterface/Views/CallFrameView.css:

(.call-frame.blackboxed > .title,):

  • UserInterface/Views/SettingsTabContentView.js:

(WI.SettingsTabContentView.prototype._createExperimentalSettingsView):

  • UserInterface/Views/SourcesNavigationSidebarPanel.js:

(WI.SourcesNavigationSidebarPanel.prototype._handleTreeSelectionDidChange):

  • UserInterface/Views/ThreadTreeElement.js:

(WI.ThreadTreeElement.prototype.refresh):

  • UserInterface/Views/TreeElement.js:

(WI.TreeElement.treeElementToggled):
This is necessary since WI.BlackboxedGroupTreeElement removes itself when expanded.

  • UserInterface/Views/Variables.css:

(:root):

LayoutTests:

Test Array.prototype.groupBy.

  • inspector/unit-tests/array-utilities-expected.txt:
  • inspector/unit-tests/array-utilities.html:
Location:
trunk
Files:
1 added
17 edited
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r272368 r272371  
     12021-02-04  Nikita Vasilyev  <nvasilyev@apple.com>
     2
     3        Web Inspector: Collapse blackboxed call frames in Sources
     4        https://bugs.webkit.org/show_bug.cgi?id=216897
     5
     6        Reviewed by Devin Rousso.
     7
     8        Test Array.prototype.groupBy.
     9
     10        * inspector/unit-tests/array-utilities-expected.txt:
     11        * inspector/unit-tests/array-utilities.html:
     12
    1132021-02-04  Aditya Keerthi  <akeerthi@apple.com>
    214
  • trunk/LayoutTests/inspector/unit-tests/array-utilities-expected.txt

    r249301 r272371  
    106106[1,2,3,4] => [[1,2],[2,3],[3,4]]
    107107
     108-- Running test case: Array.prototype.groupBy
     109[0,1,0,1,1,0,1,1,1,0] => [0,[1],0,[1,1],0,[1,1,1],0]
     110[0,1,0,1,1,0,1,1,1,0] => [0,1,0,[1,1],0,[1,1,1],0]
     111[0,1,0,1,1,0,1,1,1,0] => [0,1,0,1,1,0,[1,1,1],0]
     112[0,1,0,1,1,0,1,1,1,0] => [0,1,0,1,1,0,1,1,1,0]
     113[0,1,0,1,1,0,1,1,1] => [0,[1],0,[1,1],0,[1,1,1]]
     114[0,1,0,1,1,0,1,1,1] => [0,1,0,[1,1],0,[1,1,1]]
     115[0,1,0,1,1,0,1,1,1] => [0,1,0,1,1,0,[1,1,1]]
     116[0,1,0,1,1,0,1,1,1] => [0,1,0,1,1,0,1,1,1]
     117
    108118-- Running test case: Array.prototype.remove
    109119PASS: remove should return true when removing a value that exists.
  • trunk/LayoutTests/inspector/unit-tests/array-utilities.html

    r249301 r272371  
    221221
    222222    suite.addTestCase({
     223        name: "Array.prototype.groupBy",
     224        test() {
     225            function log(input, output) {
     226                InspectorTest.log(JSON.stringify(input) + " => " + JSON.stringify(output));
     227            }
     228
     229            let arr = [0, 1, 0, 1, 1, 0, 1, 1, 1, 0];
     230            let isOne = (x) => { return x === 1 };
     231
     232            log(arr, arr.groupBy(isOne, 1));
     233            log(arr, arr.groupBy(isOne, 2));
     234            log(arr, arr.groupBy(isOne, 3));
     235            log(arr, arr.groupBy(isOne, 4));
     236
     237            let arr2 = [0, 1, 0, 1, 1, 0, 1, 1, 1];
     238
     239            log(arr2, arr2.groupBy(isOne, 1));
     240            log(arr2, arr2.groupBy(isOne, 2));
     241            log(arr2, arr2.groupBy(isOne, 3));
     242            log(arr2, arr2.groupBy(isOne, 4));
     243
     244            return true;
     245        }
     246    });
     247
     248    suite.addTestCase({
    223249        name: "Array.prototype.remove",
    224250        test() {
  • trunk/Source/WebInspectorUI/ChangeLog

    r272232 r272371  
     12021-02-04  Nikita Vasilyev  <nvasilyev@apple.com>
     2
     3        Web Inspector: Collapse blackboxed call frames in Sources
     4        https://bugs.webkit.org/show_bug.cgi?id=216897
     5
     6        Reviewed by Devin Rousso.
     7
     8        Stack traces often have dozens of blackboxed call frames when using blackboxing for JS-frameworks such as React.js.
     9        It makes it hard to see more relevant non-framework code.
     10
     11        To improve this, introduce "Collapse blackboxed call frames" experimental setting, that collapses adjacent
     12        blackboxed call frames into expandable items.
     13
     14        * Localizations/en.lproj/localizedStrings.js:
     15        * UserInterface/Base/Setting.js:
     16        * UserInterface/Base/Utilities.js:
     17        * UserInterface/Images/TypeIcons.svg:
     18        * UserInterface/Main.html:
     19        * UserInterface/Models/CallFrame.js:
     20        (WI.CallFrame):
     21        (WI.CallFrame.prototype.get blackboxed):
     22        (WI.CallFrame.fromDebuggerPayload):
     23        (WI.CallFrame.fromPayload):
     24        * UserInterface/Views/BlackboxedGroupTreeElement.css: Added.
     25        (.tree-outline .item.blackboxed-group):
     26        (.tree-outline .item.blackboxed-group .icon):
     27        (@media (prefers-color-scheme: dark) .tree-outline .item.blackboxed-group .icon):
     28        * UserInterface/Views/BlackboxedGroupTreeElement.js: Added.
     29        (WI.BlackboxedGroupTreeElement):
     30        (WI.BlackboxedGroupTreeElement.prototype.expand):
     31        * UserInterface/Views/CallFrameTreeElement.css:
     32        (.tree-outline .item.call-frame.blackboxed:not(.selected)):
     33        * UserInterface/Views/CallFrameView.css:
     34        (.call-frame.blackboxed > .title,):
     35        * UserInterface/Views/SettingsTabContentView.js:
     36        (WI.SettingsTabContentView.prototype._createExperimentalSettingsView):
     37        * UserInterface/Views/SourcesNavigationSidebarPanel.js:
     38        (WI.SourcesNavigationSidebarPanel.prototype._handleTreeSelectionDidChange):
     39        * UserInterface/Views/ThreadTreeElement.js:
     40        (WI.ThreadTreeElement.prototype.refresh):
     41        * UserInterface/Views/TreeElement.js:
     42        (WI.TreeElement.treeElementToggled):
     43        This is necessary since `WI.BlackboxedGroupTreeElement` removes itself when expanded.
     44
     45        * UserInterface/Views/Variables.css:
     46        (:root):
     47
    1482021-02-02  BJ Burg  <bburg@apple.com>
    249
  • trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js

    r271874 r272371  
    220220localizedStrings["Blackbox Script"] = "Blackbox Script";
    221221localizedStrings["Blackbox script to ignore it when debugging"] = "Blackbox script to ignore it when debugging";
     222/* Part of the 'Blackboxed - %d call frames' label shown in the debugger call stack when paused instead of subsequent call frames that have been blackboxed. */
     223localizedStrings["Blackboxed @ Debugger Call Stack"] = "Blackboxed";
    222224localizedStrings["Block Variables"] = "Block Variables";
    223225/* Input label for the blur radius of a CSS box shadow */
     
    325327localizedStrings["Code"] = "Code";
    326328localizedStrings["Collapse All"] = "Collapse All";
     329/* Setting to collapse blackboxed call frames in the debugger. */
     330localizedStrings["Collapse blackboxed call frames @ Experimental Settings"] = "Collapse blackboxed call frames";
    327331localizedStrings["Collapse columns"] = "Collapse columns";
    328332localizedStrings["Collect garbage"] = "Collect garbage";
     
    421425localizedStrings["Debugger disabled during Timeline recording"] = "Debugger disabled during Timeline recording";
    422426localizedStrings["Debugging:"] = "Debugging:";
     427/* Category label for experimental settings related to debugging. */
     428localizedStrings["Debugging: @ Experimental Settings"] = "Debugging:";
    423429localizedStrings["Debugs"] = "Debugs";
    424430localizedStrings["Decoded"] = "Decoded";
     
    16901696localizedStrings["\u201C%s\u201D Event Fired"] = "\u201C%s\u201D Event Fired";
    16911697localizedStrings["\u201C%s\u201D Profile Recorded"] = "\u201C%s\u201D Profile Recorded";
     1698/* Part of the 'Blackboxed - %d call frame' label shown in the debugger call stack when paused instead of subsequent call frames that have been blackboxed. */
     1699localizedStrings["call frame @ Debugger Call Stack"] = "%d call frame";
     1700/* Part of the 'Blackboxed - %d call frames' label shown in the debugger call stack when paused instead of subsequent call frames that have been blackboxed. */
     1701localizedStrings["call frames @ Debugger Call Stack"] = "%d call frames";
    16921702localizedStrings["computed"] = "computed";
    16931703localizedStrings["default"] = "default";
  • trunk/Source/WebInspectorUI/UserInterface/Base/Setting.js

    r271874 r272371  
    225225    experimentalEnableStylesJumpToVariableDeclaration: new WI.Setting("experimental-styles-jump-to-variable-declaration", false),
    226226    experimentalEnableLayoutPanel: new WI.Setting("experimental-enable-layout-panel", false),
     227    experimentalCollapseBlackboxedCallFrames: new WI.Setting("experimental-collapse-blackboxed-call-frames", false),
    227228
    228229    // Protocol
  • trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js

    r270149 r272371  
    16251625});
    16261626
     1627Object.defineProperty(Array.prototype, "groupBy",
     1628{
     1629    value(groupFunction, minGroupSize = 1)
     1630    {
     1631        let result = [];
     1632        let startIndex = null;
     1633
     1634        let flush = (endIndex) => {
     1635            if (startIndex === null)
     1636                return;
     1637            let group = this.slice(startIndex, endIndex + 1);
     1638            let adjacentCount = (endIndex + 1) - startIndex;
     1639            if (adjacentCount >= minGroupSize)
     1640                result.push(group);
     1641            else
     1642                result.pushAll(group);
     1643        }
     1644
     1645        this.forEach((item, i) => {
     1646            if (groupFunction(item)) {
     1647                startIndex ??= i;
     1648                if (i === this.length - 1)
     1649                    flush(this.length - 1);
     1650            } else {
     1651                flush(i - 1);
     1652                result.push(item);
     1653                startIndex = null;
     1654            }
     1655        });
     1656
     1657        return result;
     1658    }
     1659});
     1660
    16271661Object.defineProperty(Promise, "chain",
    16281662{
  • trunk/Source/WebInspectorUI/UserInterface/Images/TypeIcons.svg

    r258900 r272371  
    291291    <g id="AuditTestGroupResult-dark" class="dark grey"><use href="#box"/><use href="#T"/></g>
    292292    <g id="AuditTestGroupResult-light" class="light grey"><use href="#box"/><use href="#T"/></g>
     293    <g id="Blackboxed-dark" class="dark grey"><use href="#box"/><use href="#question-mark"/></g>
     294    <g id="Blackboxed-light" class="light grey"><use href="#box"/><use href="#question-mark"/></g>
    293295    <g id="CallTrees-dark" class="dark grey"><use href="#box"/><use href="#calltree"/></g>
    294296    <g id="CallTrees-light" class="light grey"><use href="#box"/><use href="#calltree"/></g>
  • trunk/Source/WebInspectorUI/UserInterface/Main.html

    r271874 r272371  
    4242    <link rel="stylesheet" href="Views/BannerView.css">
    4343    <link rel="stylesheet" href="Views/BezierEditor.css">
     44    <link rel="stylesheet" href="Views/BlackboxedGroupTreeElement.css">
    4445    <link rel="stylesheet" href="Views/BlackboxSettingsView.css">
    4546    <link rel="stylesheet" href="Views/BootstrapScriptTreeElement.css">
     
    621622    <script src="Views/BezierEditor.js"></script>
    622623    <script src="Views/BlackboxSettingsView.js"></script>
     624    <script src="Views/BlackboxedGroupTreeElement.js"></script>
    623625    <script src="Views/BootstrapScriptTreeElement.js"></script>
    624626    <script src="Views/BoxModelDetailsSectionRow.js"></script>
  • trunk/Source/WebInspectorUI/UserInterface/Models/CallFrame.js

    r262806 r272371  
    2626WI.CallFrame = class CallFrame
    2727{
    28     constructor(target, id, sourceCodeLocation, functionName, thisObject, scopeChain, nativeCode, programCode, isTailDeleted)
     28    constructor(target, id, sourceCodeLocation, functionName, thisObject, scopeChain, nativeCode, programCode, isTailDeleted, blackboxed)
    2929    {
    3030        console.assert(target instanceof WI.Target);
     
    4848        this._programCode = programCode || false;
    4949        this._isTailDeleted = isTailDeleted || false;
     50        this._blackboxed = blackboxed || false;
    5051    }
    5152
     
    6162    get scopeChain() { return this._scopeChain; }
    6263    get isTailDeleted() { return this._isTailDeleted; }
     64    get blackboxed() { return this._blackboxed; }
    6365    get isConsoleEvaluation() { return this._isConsoleEvaluation; }
    6466
     
    210212        let programCode = WI.CallFrame.programCodeFromPayload(payload);
    211213        let isTailDeleted = payload.isTailDeleted;
    212         return new WI.CallFrame(target, id, sourceCodeLocation, functionName, thisObject, scopeChain, nativeCode, programCode, isTailDeleted);
     214        let blackboxed = sourceCodeLocation && !!WI.debuggerManager.blackboxDataForSourceCode(sourceCodeLocation.sourceCode);
     215        return new WI.CallFrame(target, id, sourceCodeLocation, functionName, thisObject, scopeChain, nativeCode, programCode, isTailDeleted, blackboxed);
    213216    }
    214217
     
    254257        const scopeChain = null;
    255258        const isTailDeleted = false;
    256         return new WI.CallFrame(target, id, sourceCodeLocation, functionName, thisObject, scopeChain, nativeCode, programCode, isTailDeleted);
     259        let blackboxed = sourceCodeLocation && !!WI.debuggerManager.blackboxDataForSourceCode(sourceCodeLocation.sourceCode);
     260        return new WI.CallFrame(target, id, sourceCodeLocation, functionName, thisObject, scopeChain, nativeCode, programCode, isTailDeleted, blackboxed);
    257261    }
    258262};
  • trunk/Source/WebInspectorUI/UserInterface/Views/BlackboxedGroupTreeElement.css

    r272370 r272371  
    11/*
    2  * Copyright (C) 2013-2020 Apple Inc. All rights reserved.
     2 * Copyright (C) 2020 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2424 */
    2525
    26 .call-frame {
    27     overflow: hidden;
    28     text-overflow: ellipsis;
    29     white-space: nowrap;
     26.tree-outline .item.blackboxed-group {
    3027    cursor: pointer;
     28    opacity: var(--blackboxed-tree-item-opacity);
    3129}
    3230
    33 .call-frame.blackboxed > .title,
    34 .call-frame.blackboxed:not(:hover, :focus) > .subtitle {
    35     opacity: 0.5;
    36 }
    37 
    38 .call-frame .icon {
    39     display: inline-block;
    40     vertical-align: top;
    41     width: 16px;
    42     height: 16px;
    43     margin-inline-end: 3px;
    44 }
    45 
    46 .call-frame .titles {
    47     display: inline-block;
    48 }
    49 
    50 .call-frame .subtitle,
    51 .call-frame .subtitle .source-link {
    52     color: hsla(0, 0%, 0%, 0.6);
    53     text-decoration: none;
    54 }
    55 
    56 .call-frame:hover .subtitle .source-link,
    57 .call-frame:focus .subtitle .source-link {
    58     color: hsl(210, 0%, 0%);
    59 }
    60 
    61 .call-frame .subtitle:empty {
    62     display: none;
    63 }
    64 
    65 .call-frame .subtitle {
    66     font-size: inherit;
    67 }
    68 
    69 .call-frame .colon {
    70     background-color: red;
    71 }
    72 
    73 .call-frame .separator {
    74     white-space: nowrap;
    75     color: hsla(0, 0%, 0%, 0.2);
     31.tree-outline .item.blackboxed-group .icon {
     32    content: url(../Images/TypeIcons.svg#Blackboxed-light);
    7633}
    7734
    7835@media (prefers-color-scheme: dark) {
    79     .call-frame .subtitle,
    80     .call-frame .subtitle .source-link {
    81         color: hsla(0, 0%, var(--foreground-lightness), 0.6);
    82     }
    83 
    84     .call-frame:hover .subtitle .source-link,
    85     .call-frame:focus .subtitle .source-link {
    86         color: hsl(0, 0%, var(--foreground-lightness));
    87     }
    88 
    89     .call-frame .separator {
    90         color: hsla(0, 0%, var(--foreground-lightness), 0.2);
     36    .tree-outline .item.blackboxed-group .icon {
     37        content: url(../Images/TypeIcons.svg#Blackboxed-dark);
    9138    }
    9239}
  • trunk/Source/WebInspectorUI/UserInterface/Views/CallFrameTreeElement.css

    r269166 r272371  
    100100
    101101.tree-outline .item.call-frame.blackboxed:not(.selected) {
    102     opacity: 0.5;
     102    opacity: var(--blackboxed-tree-item-opacity);
    103103}
  • trunk/Source/WebInspectorUI/UserInterface/Views/CallFrameView.css

    r269166 r272371  
    3333.call-frame.blackboxed > .title,
    3434.call-frame.blackboxed:not(:hover, :focus) > .subtitle {
    35     opacity: 0.5;
     35    opacity: var(--blackboxed-tree-item-opacity);
    3636}
    3737
  • trunk/Source/WebInspectorUI/UserInterface/Views/SettingsTabContentView.js

    r271874 r272371  
    404404        }
    405405
     406        let supportsBlackboxingScripts = WI.DebuggerManager.supportsBlackboxingScripts();
     407        if (supportsBlackboxingScripts) {
     408            experimentalSettingsView.addSetting(WI.UIString("Debugging:", "Debugging: @ Experimental Settings", "Category label for experimental settings related to debugging."), WI.settings.experimentalCollapseBlackboxedCallFrames, WI.UIString("Collapse blackboxed call frames", "Collapse blackboxed call frames @ Experimental Settings", "Setting to collapse blackboxed call frames in the debugger."));
     409            experimentalSettingsView.addSeparator();
     410        }
     411
    406412        let reloadInspectorButton = document.createElement("button");
    407413        reloadInspectorButton.textContent = WI.UIString("Reload Web Inspector");
     
    427433            listenForChange(WI.settings.experimentalEnableStylesJumpToVariableDeclaration);
    428434        }
     435
     436        if (supportsBlackboxingScripts)
     437            listenForChange(WI.settings.experimentalCollapseBlackboxedCallFrames);
    429438
    430439        this._createReferenceLink(experimentalSettingsView);
  • trunk/Source/WebInspectorUI/UserInterface/Views/SourcesNavigationSidebarPanel.js

    r270604 r272371  
    19881988        }
    19891989
     1990        if (treeElement instanceof WI.BlackboxedGroupTreeElement)
     1991            return;
     1992
    19901993        console.error("Unknown tree element", treeElement);
    19911994    }
  • trunk/Source/WebInspectorUI/UserInterface/Views/ThreadTreeElement.js

    r269359 r272371  
    5757        let activeCallFrameTreeElement = null;
    5858
    59         for (let callFrame of callFrames) {
    60             let callFrameTreeElement = new WI.CallFrameTreeElement(callFrame);
    61             if (callFrame === activeCallFrame)
    62                 activeCallFrameTreeElement = callFrameTreeElement;
    63             this.appendChild(callFrameTreeElement);
    64         }
     59        let renderCallFrames = (callFrames, shouldSelectActiveFrame) => {
     60            if (WI.settings.experimentalCollapseBlackboxedCallFrames.value)
     61                callFrames = callFrames.groupBy((callFrame) => callFrame.blackboxed);
     62
     63            for (let callFrameOrBlackboxedGroup of callFrames) {
     64                if (Array.isArray(callFrameOrBlackboxedGroup)) {
     65                    this.appendChild(new WI.BlackboxedGroupTreeElement(callFrameOrBlackboxedGroup));
     66                    continue;
     67                }
     68                let callFrameTreeElement = new WI.CallFrameTreeElement(callFrameOrBlackboxedGroup);
     69                if (shouldSelectActiveFrame && callFrameOrBlackboxedGroup === activeCallFrame)
     70                    activeCallFrameTreeElement = callFrameTreeElement;
     71                this.appendChild(callFrameTreeElement);
     72            }
     73        };
     74
     75        renderCallFrames(callFrames, true);
    6576
    6677        if (activeCallFrameTreeElement) {
     
    90101
    91102            let startIndex = currentStackTrace.topCallFrameIsBoundary ? 1 : 0;
    92             for (let i = startIndex; i < currentStackTrace.callFrames.length; ++i)
    93                 this.appendChild(new WI.CallFrameTreeElement(currentStackTrace.callFrames[i]));
     103            renderCallFrames(startIndex ? currentStackTrace.callFrames.slice(startIndex) : currentStackTrace.callFrames);
    94104
    95105            if (currentStackTrace.truncated) {
  • trunk/Source/WebInspectorUI/UserInterface/Views/TreeElement.js

    r258536 r272371  
    325325        }
    326326
    327         if (!treeElement.treeOutline.selectable)
     327        if (treeElement.treeOutline && !treeElement.treeOutline.selectable)
    328328            treeElement.treeOutline.dispatchEventToListeners(WI.TreeOutline.Event.ElementClicked, {treeElement});
    329329    }
  • trunk/Source/WebInspectorUI/UserInterface/Views/Variables.css

    r268473 r272371  
    216216
    217217    --image-button-navigation-item-width: 26px;
     218
     219    --blackboxed-tree-item-opacity: 0.5;
    218220}
    219221
Note: See TracChangeset for help on using the changeset viewer.