Title: [239858] trunk
Revision
239858
Author
drou...@apple.com
Date
2019-01-10 21:54:54 -0800 (Thu, 10 Jan 2019)

Log Message

Web Inspector: Audit: allow audits to be enabled/disabled
https://bugs.webkit.org/show_bug.cgi?id=192210
<rdar://problem/46423583>

Reviewed by Joseph Pecoraro.

Source/WebInspectorUI:

* UserInterface/Controllers/AuditManager.js:
(WI.AuditManager.prototype.get editing): Added.
(WI.AuditManager.prototype.set editing): Added.
(WI.AuditManager.prototype.stop):
(WI.AuditManager.prototype.addDefaultTestsIfNeeded):
Since default audits aren't stored, keep a list of disabled default tests in a `WI.Setting`.

* UserInterface/Models/AuditTestBase.js:
(WI.AuditTestBase):
(WI.AuditTestBase.prototype.get disabled): Added.
(WI.AuditTestBase.prototype.set disabled): Added.
(WI.AuditTestBase.prototype.async start):
(WI.AuditTestBase.prototype.stop):
(WI.AuditTestBase.toJSON):

* UserInterface/Models/AuditTestCase.js:
(WI.AuditTestCase):
(WI.AuditTestCase.async fromPayload):
(WI.AuditTestCase.prototype.toJSON):

* UserInterface/Models/AuditTestGroup.js:
(WI.AuditTestGroup):
(WI.AuditTestGroup.async fromPayload):
(WI.AuditTestGroup.prototype.get disabled): Added.
(WI.AuditTestGroup.prototype.set disabled): Added.
(WI.AuditTestGroup.prototype.toJSON):
(WI.AuditTestGroup.prototype.async run):
(WI.AuditTestGroup.prototype._handleTestDisabledChanged): Added.
(WI.AuditTestGroup.prototype._handleTestProgress):
Propagate `disabled` changes to all sub-tests, unless the change was caused by one of the
sub-tests, in which case we are now in an intermediate state.

* UserInterface/Views/AuditNavigationSidebarPanel.js:
(WI.AuditNavigationSidebarPanel):
(WI.AuditNavigationSidebarPanel.prototype.showDefaultContentView):
(WI.AuditNavigationSidebarPanel.prototype.initialLayout):
(WI.AuditNavigationSidebarPanel.prototype.hasCustomFilters): Added.
(WI.AuditNavigationSidebarPanel.prototype.matchTreeElementAgainstCustomFilters): Added.
(WI.AuditNavigationSidebarPanel.prototype._addTest):
(WI.AuditNavigationSidebarPanel.prototype._addResult):
(WI.AuditNavigationSidebarPanel.prototype._updateStartStopButtonNavigationItemState):
(WI.AuditNavigationSidebarPanel.prototype._updateEditButtonNavigationItemState): Added.
(WI.AuditNavigationSidebarPanel.prototype._handleAuditManagerEditingChanged): Added.
(WI.AuditNavigationSidebarPanel.prototype._handleAuditTestRemoved):
(WI.AuditNavigationSidebarPanel.prototype._handleAuditTestScheduled):
(WI.AuditNavigationSidebarPanel.prototype._treeSelectionDidChange):
(WI.AuditNavigationSidebarPanel.prototype._handleEditButtonNavigationItemClicked): Added.
* UserInterface/Views/AuditNavigationSidebarPanel.css:
(.sidebar > .panel.navigation.audit > .content):
(.sidebar > .panel.navigation.audit > .content > .tree-outline): Added.
(.sidebar > .panel.navigation.audit > .content .edit-audits:not(.disabled):active): Added.
(.sidebar > .panel.navigation.audit > .content .edit-audits:not(.disabled).activated): Added.
(.sidebar > .panel.navigation.audit > .content .edit-audits:not(.disabled).activated:active): Added.
(.sidebar > .panel.navigation.audit > .content .edit-audits.disabled): Added.
(.finish-editing-audits-placeholder.message-text-view .navigation-item-help .navigation-bar): Added.
Leverage custom filters to ensure that disabled audits arent shown when not editing and that
result tree elements aren't shown while editing.

* UserInterface/Views/AuditTestGroupContentView.js:
(WI.AuditTestGroupContentView.prototype.shown):

* UserInterface/Views/AuditTreeElement.js:
(WI.AuditTreeElement.prototype.onattach):
(WI.AuditTreeElement.prototype.canSelectOnMouseDown): Added.
(WI.AuditTreeElement.prototype._updateTestGroupDisabled): Added.
(WI.AuditTreeElement.prototype._handleTestDisabledChanged): Added.
(WI.AuditTreeElement.prototype._handleManagerEditingChanged): Added.
* UserInterface/Views/AuditTreeElement.css:
(.tree-outline .item.audit > .status:not(:hover) > img.show-on-hover, .tree-outline .item.audit.test-group.expanded:not(.editing-audits) > .status:not(:hover)): Added.
(.tree-outline .item.audit.manager-active > .status > img.show-on-hover, .tree-outline .item.audit.test-group.expanded:not(.editing-audits) > .status:hover > :not(img), .tree-outline .item.audit.test-group-result.expanded > .status): Added.
(.tree-outline .item.audit > .status:not(:hover) > img.show-on-hover, .tree-outline .item.audit.test-group.expanded > .status:not(:hover)): Deleted.
(.tree-outline .item.audit.manager-active > .status > img.show-on-hover, .tree-outline .item.audit.test-group.expanded > .status:hover > :not(img), .tree-outline .item.audit.test-group-result.expanded > .status): Deleted.
Prevent selection and running when editing.

* UserInterface/Views/TreeOutline.css:
(.tree-outline .children.expanded:not([hidden])): Added.
(.tree-outline .children.expanded): Deleted.

* UserInterface/Base/ObjectStore.js:
(WI.ObjectStore._open):
Batch operations together to help avoid multiple simultaneous `indexedDB.open` calls. This
should also help preserve the order of operations, as once the database is open, operations
are executed in the order they were enqueued.

(WI.ObjectStore.prototype.async.addObject):
Pass a unique `Symbol` to the `toJSON` call on the given object so that the object can save
additional values that wouldn't normally be saved. This doesn't conflict with normal usage
of `toJSON` (e.g. `JSON.stringify`) because that case also passes in a value:
 - `undefined`, if it was called directly on the object
 - the key for this object in the containing object
 - the index of this object in the containing array
In any case, the value can never equal the unique `Symbol`, so it's guaranteed that the code
will only run for `WI.ObjectStore` operations.

(WI.ObjectStore.prototype.async.clear): Added.

* Localizations/en.lproj/localizedStrings.js:

LayoutTests:

* inspector/unit-tests/objectStore/clear.html: Added.
* inspector/unit-tests/objectStore/clear-expected.txt: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (239857 => 239858)


--- trunk/LayoutTests/ChangeLog	2019-01-11 04:46:23 UTC (rev 239857)
+++ trunk/LayoutTests/ChangeLog	2019-01-11 05:54:54 UTC (rev 239858)
@@ -1,3 +1,14 @@
+2019-01-10  Devin Rousso  <drou...@apple.com>
+
+        Web Inspector: Audit: allow audits to be enabled/disabled
+        https://bugs.webkit.org/show_bug.cgi?id=192210
+        <rdar://problem/46423583>
+
+        Reviewed by Joseph Pecoraro.
+
+        * inspector/unit-tests/objectStore/clear.html: Added.
+        * inspector/unit-tests/objectStore/clear-expected.txt: Added.
+
 2019-01-10  Justin Fan  <justin_...@apple.com>
 
         [WebGPU] WebGPUBindGroup and device::createBindGroup prototype

Added: trunk/LayoutTests/inspector/unit-tests/objectStore/clear-expected.txt (0 => 239858)


--- trunk/LayoutTests/inspector/unit-tests/objectStore/clear-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/inspector/unit-tests/objectStore/clear-expected.txt	2019-01-11 05:54:54 UTC (rev 239858)
@@ -0,0 +1,12 @@
+Tests WI.ObjectStore.prototype.clear.
+
+
+== Running test suite: WI.ObjectStore.prototype.clear
+-- Running test case: WI.ObjectStore.prototype.clear.Empty
+[]
+[]
+
+-- Running test case: WI.ObjectStore.prototype.clear.NotEmpty
+[true,1,"foo",["bar"],{"a":1}]
+[]
+

Added: trunk/LayoutTests/inspector/unit-tests/objectStore/clear.html (0 => 239858)


--- trunk/LayoutTests/inspector/unit-tests/objectStore/clear.html	                        (rev 0)
+++ trunk/LayoutTests/inspector/unit-tests/objectStore/clear.html	2019-01-11 05:54:54 UTC (rev 239858)
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<script src=""
+<script>
+function test()
+{
+    let suite = InspectorTest.ObjectStore.createSuite("WI.ObjectStore.prototype.clear");
+
+    function testClear(name, {objects}) {
+        InspectorTest.ObjectStore.wrapTest(name, async function() {
+            let objectStore = InspectorTest.ObjectStore.createObjectStore({autoIncrement: true});
+
+            for (let object of objects)
+                await objectStore.add(object);
+
+            await InspectorTest.ObjectStore.logValues();
+
+            await objectStore.clear();
+        });
+    }
+
+    testClear("WI.ObjectStore.prototype.clear.Empty", {
+        objects: [],
+    });
+
+    testClear("WI.ObjectStore.prototype.clear.NotEmpty", {
+        objects: [true, 1, "foo", ["bar"], {a: 1}],
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body _onload_="runTest()">
+    <p>Tests WI.ObjectStore.prototype.clear.</p>
+</body>
+</html>

Modified: trunk/Source/WebInspectorUI/ChangeLog (239857 => 239858)


--- trunk/Source/WebInspectorUI/ChangeLog	2019-01-11 04:46:23 UTC (rev 239857)
+++ trunk/Source/WebInspectorUI/ChangeLog	2019-01-11 05:54:54 UTC (rev 239858)
@@ -1,3 +1,109 @@
+2019-01-10  Devin Rousso  <drou...@apple.com>
+
+        Web Inspector: Audit: allow audits to be enabled/disabled
+        https://bugs.webkit.org/show_bug.cgi?id=192210
+        <rdar://problem/46423583>
+
+        Reviewed by Joseph Pecoraro.
+
+        * UserInterface/Controllers/AuditManager.js:
+        (WI.AuditManager.prototype.get editing): Added.
+        (WI.AuditManager.prototype.set editing): Added.
+        (WI.AuditManager.prototype.stop):
+        (WI.AuditManager.prototype.addDefaultTestsIfNeeded):
+        Since default audits aren't stored, keep a list of disabled default tests in a `WI.Setting`.
+
+        * UserInterface/Models/AuditTestBase.js:
+        (WI.AuditTestBase):
+        (WI.AuditTestBase.prototype.get disabled): Added.
+        (WI.AuditTestBase.prototype.set disabled): Added.
+        (WI.AuditTestBase.prototype.async start):
+        (WI.AuditTestBase.prototype.stop):
+        (WI.AuditTestBase.toJSON):
+
+        * UserInterface/Models/AuditTestCase.js:
+        (WI.AuditTestCase):
+        (WI.AuditTestCase.async fromPayload):
+        (WI.AuditTestCase.prototype.toJSON):
+
+        * UserInterface/Models/AuditTestGroup.js:
+        (WI.AuditTestGroup):
+        (WI.AuditTestGroup.async fromPayload):
+        (WI.AuditTestGroup.prototype.get disabled): Added.
+        (WI.AuditTestGroup.prototype.set disabled): Added.
+        (WI.AuditTestGroup.prototype.toJSON):
+        (WI.AuditTestGroup.prototype.async run):
+        (WI.AuditTestGroup.prototype._handleTestDisabledChanged): Added.
+        (WI.AuditTestGroup.prototype._handleTestProgress):
+        Propagate `disabled` changes to all sub-tests, unless the change was caused by one of the
+        sub-tests, in which case we are now in an intermediate state.
+
+        * UserInterface/Views/AuditNavigationSidebarPanel.js:
+        (WI.AuditNavigationSidebarPanel):
+        (WI.AuditNavigationSidebarPanel.prototype.showDefaultContentView):
+        (WI.AuditNavigationSidebarPanel.prototype.initialLayout):
+        (WI.AuditNavigationSidebarPanel.prototype.hasCustomFilters): Added.
+        (WI.AuditNavigationSidebarPanel.prototype.matchTreeElementAgainstCustomFilters): Added.
+        (WI.AuditNavigationSidebarPanel.prototype._addTest):
+        (WI.AuditNavigationSidebarPanel.prototype._addResult):
+        (WI.AuditNavigationSidebarPanel.prototype._updateStartStopButtonNavigationItemState):
+        (WI.AuditNavigationSidebarPanel.prototype._updateEditButtonNavigationItemState): Added.
+        (WI.AuditNavigationSidebarPanel.prototype._handleAuditManagerEditingChanged): Added.
+        (WI.AuditNavigationSidebarPanel.prototype._handleAuditTestRemoved):
+        (WI.AuditNavigationSidebarPanel.prototype._handleAuditTestScheduled):
+        (WI.AuditNavigationSidebarPanel.prototype._treeSelectionDidChange):
+        (WI.AuditNavigationSidebarPanel.prototype._handleEditButtonNavigationItemClicked): Added.
+        * UserInterface/Views/AuditNavigationSidebarPanel.css:
+        (.sidebar > .panel.navigation.audit > .content):
+        (.sidebar > .panel.navigation.audit > .content > .tree-outline): Added.
+        (.sidebar > .panel.navigation.audit > .content .edit-audits:not(.disabled):active): Added.
+        (.sidebar > .panel.navigation.audit > .content .edit-audits:not(.disabled).activated): Added.
+        (.sidebar > .panel.navigation.audit > .content .edit-audits:not(.disabled).activated:active): Added.
+        (.sidebar > .panel.navigation.audit > .content .edit-audits.disabled): Added.
+        (.finish-editing-audits-placeholder.message-text-view .navigation-item-help .navigation-bar): Added.
+        Leverage custom filters to ensure that disabled audits arent shown when not editing and that
+        result tree elements aren't shown while editing.
+
+        * UserInterface/Views/AuditTestGroupContentView.js:
+        (WI.AuditTestGroupContentView.prototype.shown):
+
+        * UserInterface/Views/AuditTreeElement.js:
+        (WI.AuditTreeElement.prototype.onattach):
+        (WI.AuditTreeElement.prototype.canSelectOnMouseDown): Added.
+        (WI.AuditTreeElement.prototype._updateTestGroupDisabled): Added.
+        (WI.AuditTreeElement.prototype._handleTestDisabledChanged): Added.
+        (WI.AuditTreeElement.prototype._handleManagerEditingChanged): Added.
+        * UserInterface/Views/AuditTreeElement.css:
+        (.tree-outline .item.audit > .status:not(:hover) > img.show-on-hover, .tree-outline .item.audit.test-group.expanded:not(.editing-audits) > .status:not(:hover)): Added.
+        (.tree-outline .item.audit.manager-active > .status > img.show-on-hover, .tree-outline .item.audit.test-group.expanded:not(.editing-audits) > .status:hover > :not(img), .tree-outline .item.audit.test-group-result.expanded > .status): Added.
+        (.tree-outline .item.audit > .status:not(:hover) > img.show-on-hover, .tree-outline .item.audit.test-group.expanded > .status:not(:hover)): Deleted.
+        (.tree-outline .item.audit.manager-active > .status > img.show-on-hover, .tree-outline .item.audit.test-group.expanded > .status:hover > :not(img), .tree-outline .item.audit.test-group-result.expanded > .status): Deleted.
+        Prevent selection and running when editing.
+
+        * UserInterface/Views/TreeOutline.css:
+        (.tree-outline .children.expanded:not([hidden])): Added.
+        (.tree-outline .children.expanded): Deleted.
+
+        * UserInterface/Base/ObjectStore.js:
+        (WI.ObjectStore._open):
+        Batch operations together to help avoid multiple simultaneous `indexedDB.open` calls. This
+        should also help preserve the order of operations, as once the database is open, operations
+        are executed in the order they were enqueued.
+
+        (WI.ObjectStore.prototype.async.addObject):
+        Pass a unique `Symbol` to the `toJSON` call on the given object so that the object can save
+        additional values that wouldn't normally be saved. This doesn't conflict with normal usage
+        of `toJSON` (e.g. `JSON.stringify`) because that case also passes in a value:
+         - `undefined`, if it was called directly on the object
+         - the key for this object in the containing object
+         - the index of this object in the containing array
+        In any case, the value can never equal the unique `Symbol`, so it's guaranteed that the code
+        will only run for `WI.ObjectStore` operations.
+
+        (WI.ObjectStore.prototype.async.clear): Added.
+
+        * Localizations/en.lproj/localizedStrings.js:
+
 2019-01-09  Devin Rousso  <drou...@apple.com>
 
         Web Inspector: Protocol Logging: log messages as objects if inspector^2 is open

Modified: trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js (239857 => 239858)


--- trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js	2019-01-11 04:46:23 UTC (rev 239857)
+++ trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js	2019-01-11 05:54:54 UTC (rev 239858)
@@ -324,6 +324,7 @@
 localizedStrings["Edit \u201Cspring\u201D function"] = "Edit \u201Cspring\u201D function";
 localizedStrings["Edit configuration"] = "Edit configuration";
 localizedStrings["Edit custom gradient"] = "Edit custom gradient";
+localizedStrings["Editing audits"] = "Editing audits";
 localizedStrings["Element"] = "Element";
 localizedStrings["Element clips compositing descendants"] = "Element clips compositing descendants";
 localizedStrings["Element has CSS blending applied and composited descendants"] = "Element has CSS blending applied and composited descendants";
@@ -688,6 +689,7 @@
 localizedStrings["Press %s to import a test or result file"] = "Press %s to import a test or result file";
 localizedStrings["Press %s to load a recording from file."] = "Press %s to load a recording from file.";
 localizedStrings["Press %s to start running the audit"] = "Press %s to start running the audit";
+localizedStrings["Press %s to stop editing"] = "Press %s to stop editing";
 localizedStrings["Pressed"] = "Pressed";
 localizedStrings["Pretty print"] = "Pretty print";
 localizedStrings["Preview"] = "Preview";

Modified: trunk/Source/WebInspectorUI/UserInterface/Base/ObjectStore.js (239857 => 239858)


--- trunk/Source/WebInspectorUI/UserInterface/Base/ObjectStore.js	2019-01-11 04:46:23 UTC (rev 239857)
+++ trunk/Source/WebInspectorUI/UserInterface/Base/ObjectStore.js	2019-01-11 05:54:54 UTC (rev 239858)
@@ -52,6 +52,13 @@
             return;
         }
 
+        if (Array.isArray(WI.ObjectStore._databaseCallbacks)) {
+            WI.ObjectStore._databaseCallbacks.push(callback);
+            return;
+        }
+
+        WI.ObjectStore._databaseCallbacks = [callback];
+
         const version = 1; // Increment this for every edit to `WI.objectStores`.
 
         let databaseRequest = indexedDB.open(WI.ObjectStore._databaseName, version);
@@ -81,7 +88,10 @@
                 WI.ObjectStore._database = null;
             });
 
-            callback(WI.ObjectStore._database);
+            for (let databaseCallback of WI.ObjectStore._databaseCallbacks)
+                databaseCallback(WI.ObjectStore._database);
+
+            WI.ObjectStore._databaseCallbacks = null;
         });
     }
 
@@ -118,7 +128,7 @@
             return undefined;
 
         console.assert(typeof object.toJSON === "function", "ObjectStore cannot store an object without JSON serialization", object.constructor.name);
-        let result = await this.add(object.toJSON(), ...args);
+        let result = await this.add(object.toJSON(WI.ObjectStore.toJSONSymbol), ...args);
         this.associateObject(object, args[0], result);
         return result;
     }
@@ -139,6 +149,14 @@
         return this.delete(this._resolveKeyPath(object).value, ...args);
     }
 
+    async clear(...args)
+    {
+        if (!WI.ObjectStore.supported())
+            return undefined;
+
+        return this._operation("readwrite", (objectStore) => objectStore.clear(...args));
+    }
+
     // Private
 
     _resolveKeyPath(object, keyPath)
@@ -203,7 +221,10 @@
 };
 
 WI.ObjectStore._database = null;
+WI.ObjectStore._databaseCallbacks = null;
 
+WI.ObjectStore.toJSONSymbol = Symbol("ObjectStore-toJSON");
+
 // Be sure to update the `version` above when making changes.
 WI.objectStores = {
     audits: new WI.ObjectStore("audit-manager-tests", {keyPath: "__id", autoIncrement: true}),

Modified: trunk/Source/WebInspectorUI/UserInterface/Controllers/AuditManager.js (239857 => 239858)


--- trunk/Source/WebInspectorUI/UserInterface/Controllers/AuditManager.js	2019-01-11 04:46:23 UTC (rev 239857)
+++ trunk/Source/WebInspectorUI/UserInterface/Controllers/AuditManager.js	2019-01-11 05:54:54 UTC (rev 239858)
@@ -35,6 +35,8 @@
         this._runningState = WI.AuditManager.RunningState.Inactive;
         this._runningTests = [];
 
+        this._disabledDefaultTestsSetting = new WI.Setting("audit-disabled-default-tests", []);
+
         WI.Frame.addEventListener(WI.Frame.Event.MainResourceDidChange, this._handleFrameMainResourceDidChange, this);
     }
 
@@ -52,6 +54,51 @@
     get results() { return this._results; }
     get runningState() { return this._runningState; }
 
+    get editing()
+    {
+        return this._runningState === WI.AuditManager.RunningState.Disabled;
+    }
+
+    set editing(editing)
+    {
+        console.assert(this._runningState === WI.AuditManager.RunningState.Disabled || this._runningState === WI.AuditManager.RunningState.Inactive);
+        if (this._runningState !== WI.AuditManager.RunningState.Disabled && this._runningState !== WI.AuditManager.RunningState.Inactive)
+            return;
+
+        let runningState = editing ? WI.AuditManager.RunningState.Disabled : WI.AuditManager.RunningState.Inactive;
+        console.assert(runningState !== this._runningState);
+        if (runningState === this._runningState)
+            return;
+
+        this._runningState = runningState;
+
+        this.dispatchEventToListeners(WI.AuditManager.Event.EditingChanged);
+
+        if (!this.editing) {
+            WI.objectStores.audits.clear();
+
+            let disabledDefaultTests = [];
+            let saveDisabledDefaultTest = (test) => {
+                if (test.disabled)
+                    disabledDefaultTests.push(test.name);
+
+                if (test instanceof WI.AuditTestGroup) {
+                    for (let child of test.tests)
+                        saveDisabledDefaultTest(child);
+                }
+            };
+
+            for (let test of this._tests) {
+                if (test.__default)
+                    saveDisabledDefaultTest(test);
+                else
+                    WI.objectStores.audits.addObject(test);
+            }
+
+            this._disabledDefaultTestsSetting.value = disabledDefaultTests;
+        }
+    }
+
     async start(tests)
     {
         console.assert(this._runningState === WI.AuditManager.RunningState.Inactive);
@@ -97,10 +144,10 @@
         if (this._runningState !== WI.AuditManager.RunningState.Active)
             return;
 
+        this._runningState = WI.AuditManager.RunningState.Stopping;
+
         for (let test of this._runningTests)
             test.stop();
-
-        this._runningState = WI.AuditManager.RunningState.Stopping;
     }
 
     async processJSON({json, error})
@@ -253,7 +300,19 @@
             ], {description: WI.UIString("Tests for ways to improve accessibility.")}),
         ];
 
+        let checkDisabledDefaultTest = (test) => {
+            if (this._disabledDefaultTestsSetting.value.includes(test.name))
+                test.disabled = true;
+
+            if (test instanceof WI.AuditTestGroup) {
+                for (let child of test.tests)
+                    checkDisabledDefaultTest(child);
+            }
+        };
+
         for (let test of defaultTests) {
+            checkDisabledDefaultTest(test);
+
             test.__default = true;
             this._addTest(test);
         }
@@ -261,6 +320,7 @@
 };
 
 WI.AuditManager.RunningState = {
+    Disabled: "disabled",
     Inactive: "inactive",
     Active: "active",
     Stopping: "stopping",
@@ -267,6 +327,7 @@
 };
 
 WI.AuditManager.Event = {
+    EditingChanged: "audit-manager-editing-changed",
     TestAdded: "audit-manager-test-added",
     TestCompleted: "audit-manager-test-completed",
     TestRemoved: "audit-manager-test-removed",

Modified: trunk/Source/WebInspectorUI/UserInterface/Models/AuditTestBase.js (239857 => 239858)


--- trunk/Source/WebInspectorUI/UserInterface/Models/AuditTestBase.js	2019-01-11 04:46:23 UTC (rev 239857)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/AuditTestBase.js	2019-01-11 05:54:54 UTC (rev 239858)
@@ -25,17 +25,21 @@
 
 WI.AuditTestBase = class AuditTestBase extends WI.Object
 {
-    constructor(name, {description} = {})
+    constructor(name, {description, disabled} = {})
     {
         console.assert(typeof name === "string");
         console.assert(!description || typeof description === "string");
+        console.assert(disabled === undefined || typeof disabled === "boolean");
 
         super();
 
+        // This class should not be instantiated directly. Create a concrete subclass instead.
+        console.assert(this.constructor !== WI.AuditTestBase && this instanceof WI.AuditTestBase);
+
         this._name = name;
         this._description = description || null;
 
-        this._runningState = WI.AuditManager.RunningState.Inactive;
+        this._runningState = disabled ? WI.AuditManager.RunningState.Disabled : WI.AuditManager.RunningState.Inactive;
         this._result = null;
     }
 
@@ -46,10 +50,33 @@
     get runningState() { return this._runningState; }
     get result() { return this._result; }
 
+    get disabled()
+    {
+        return this._runningState === WI.AuditManager.RunningState.Disabled;
+    }
+
+    set disabled(disabled)
+    {
+        console.assert(this._runningState === WI.AuditManager.RunningState.Disabled || this._runningState === WI.AuditManager.RunningState.Inactive);
+        if (this._runningState !== WI.AuditManager.RunningState.Disabled && this._runningState !== WI.AuditManager.RunningState.Inactive)
+            return;
+
+        let runningState = disabled ? WI.AuditManager.RunningState.Disabled : WI.AuditManager.RunningState.Inactive;
+        if (runningState === this._runningState)
+            return;
+
+        this._runningState = runningState;
+
+        this.dispatchEventToListeners(WI.AuditTestBase.Event.DisabledChanged);
+    }
+
     async start()
     {
         // Called from WI.AuditManager.
 
+        if (this.disabled)
+            return;
+
         console.assert(WI.auditManager.runningState === WI.AuditManager.RunningState.Active);
 
         console.assert(this._runningState === WI.AuditManager.RunningState.Inactive);
@@ -69,8 +96,11 @@
     {
         // Called from WI.AuditManager.
 
-        console.assert(this._runningState !== WI.AuditManager.RunningState.Inactive);
+        if (this.disabled)
+            return;
 
+        console.assert(WI.auditManager.runningState === WI.AuditManager.RunningState.Stopping);
+
         if (this._runningState !== WI.AuditManager.RunningState.Active)
             return;
 
@@ -96,7 +126,7 @@
         cookie["audit-" + this.constructor.TypeIdentifier + "-name"] = this._name;
     }
 
-    toJSON()
+    toJSON(key)
     {
         let json = {
             type: this.constructor.TypeIdentifier,
@@ -104,6 +134,8 @@
         };
         if (this._description)
             json.description = this._description;
+        if (key === WI.ObjectStore.toJSONSymbol)
+            json.disabled = this.disabled;
         return json;
     }
 
@@ -117,6 +149,7 @@
 
 WI.AuditTestBase.Event = {
     Completed: "audit-test-base-completed",
+    DisabledChanged: "audit-test-base-disabled-changed",
     Progress: "audit-test-base-progress",
     ResultCleared: "audit-test-base-result-cleared",
     Scheduled: "audit-test-base-scheduled",

Modified: trunk/Source/WebInspectorUI/UserInterface/Models/AuditTestCase.js (239857 => 239858)


--- trunk/Source/WebInspectorUI/UserInterface/Models/AuditTestCase.js	2019-01-11 04:46:23 UTC (rev 239857)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/AuditTestCase.js	2019-01-11 05:54:54 UTC (rev 239858)
@@ -25,11 +25,11 @@
 
 WI.AuditTestCase = class AuditTestCase extends WI.AuditTestBase
 {
-    constructor(name, test, {description} = {})
+    constructor(name, test, options = {})
     {
         console.assert(typeof test === "string");
 
-        super(name, {description});
+        super(name, options);
 
         this._test = test;
     }
@@ -41,7 +41,7 @@
         if (typeof payload !== "object" || payload === null)
             return null;
 
-        let {type, name, test, description} = payload;
+        let {type, name, test, description, disabled} = payload;
 
         if (type !== WI.AuditTestCase.TypeIdentifier)
             return null;
@@ -55,6 +55,8 @@
         let options = {};
         if (typeof description === "string")
             options.description = description;
+        if (typeof disabled === "boolean")
+            options.disabled = disabled;
 
         return new WI.AuditTestCase(name, test, options);
     }
@@ -63,9 +65,9 @@
 
     get test() { return this._test; }
 
-    toJSON()
+    toJSON(key)
     {
-        let json = super.toJSON();
+        let json = super.toJSON(key);
         json.test = this._test;
         return json;
     }

Modified: trunk/Source/WebInspectorUI/UserInterface/Models/AuditTestGroup.js (239857 => 239858)


--- trunk/Source/WebInspectorUI/UserInterface/Models/AuditTestGroup.js	2019-01-11 04:46:23 UTC (rev 239857)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/AuditTestGroup.js	2019-01-11 05:54:54 UTC (rev 239858)
@@ -25,16 +25,25 @@
 
 WI.AuditTestGroup = class AuditTestGroup extends WI.AuditTestBase
 {
-    constructor(name, tests, {description} = {})
+    constructor(name, tests, options = {})
     {
         console.assert(Array.isArray(tests));
 
-        super(name, {description});
+        // Set disabled once `_tests` is set so that it propagates.
+        let disabled = options.disabled;
+        options.disabled = false;
 
+        super(name, options);
+
         this._tests = tests;
+        this._preventDisabledPropagation = false;
 
+        if (disabled)
+            this.disabled = disabled;
+
         for (let test of this._tests) {
             test.addEventListener(WI.AuditTestBase.Event.Completed, this._handleTestCompleted, this);
+            test.addEventListener(WI.AuditTestBase.Event.DisabledChanged, this._handleTestDisabledChanged, this);
             test.addEventListener(WI.AuditTestBase.Event.Progress, this._handleTestProgress, this);
         }
     }
@@ -46,7 +55,7 @@
         if (typeof payload !== "object" || payload === null)
             return null;
 
-        let {type, name, tests, description} = payload;
+        let {type, name, tests, description, disabled} = payload;
 
         if (type !== WI.AuditTestGroup.TypeIdentifier)
             return null;
@@ -75,6 +84,8 @@
         let options = {};
         if (typeof description === "string")
             options.description = description;
+        if (typeof disabled === "boolean")
+            options.disabled = disabled;
 
         return new WI.AuditTestGroup(name, tests, options);
     }
@@ -83,6 +94,21 @@
 
     get tests() { return this._tests; }
 
+    get disabled()
+    {
+        return super.disabled;
+    }
+
+    set disabled(disabled)
+    {
+        if (!this._preventDisabledPropagation) {
+            for (let test of this._tests)
+                test.disabled = disabled;
+        }
+
+        super.disabled = disabled;
+    }
+
     stop()
     {
         // Called from WI.AuditManager.
@@ -107,10 +133,10 @@
         });
     }
 
-    toJSON()
+    toJSON(key)
     {
-        let json = super.toJSON();
-        json.tests = this._tests.map((testCase) => testCase.toJSON());
+        let json = super.toJSON(key);
+        json.tests = this._tests.map((testCase) => testCase.toJSON(key));
         return json;
     }
 
@@ -121,6 +147,8 @@
         let count = this._tests.length;
         for (let index = 0; index < count && this._runningState === WI.AuditManager.RunningState.Active; ++index) {
             let test = this._tests[index];
+            if (test.disabled)
+                continue;
 
             await test.start();
 
@@ -153,6 +181,21 @@
         this.dispatchEventToListeners(WI.AuditTestBase.Event.Completed);
     }
 
+    _handleTestDisabledChanged(event)
+    {
+        let enabledTestCount = this._tests.filter((test) => !test.disabled).length;
+        if (event.target.disabled && !enabledTestCount)
+            this.disabled = true;
+        else if (!event.target.disabled && enabledTestCount === 1) {
+            this._preventDisabledPropagation = true;
+            this.disabled = false;
+            this._preventDisabledPropagation = false;
+        } else {
+            // Don't change `disabled`, as we're currently in an "indeterminate" state.
+            this.dispatchEventToListeners(WI.AuditTestBase.Event.DisabledChanged);
+        }
+    }
+
     _handleTestProgress(event)
     {
         if (this._runningState !== WI.AuditManager.RunningState.Active)
@@ -161,6 +204,9 @@
         let walk = (tests) => {
             let count = 0;
             for (let test of tests) {
+                if (test.disabled)
+                    continue;
+
                 if (test instanceof WI.AuditTestCase)
                     ++count;
                 else if (test instanceof WI.AuditTestGroup)
@@ -170,8 +216,8 @@
         };
 
         this.dispatchEventToListeners(WI.AuditTestBase.Event.Progress, {
-            index: event.data.index + walk(this.tests.slice(0, this.tests.indexOf(event.target))),
-            count: walk(this.tests),
+            index: event.data.index + walk(this._tests.slice(0, this._tests.indexOf(event.target))),
+            count: walk(this._tests),
         });
     }
 };

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/AuditNavigationSidebarPanel.css (239857 => 239858)


--- trunk/Source/WebInspectorUI/UserInterface/Views/AuditNavigationSidebarPanel.css	2019-01-11 04:46:23 UTC (rev 239857)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/AuditNavigationSidebarPanel.css	2019-01-11 05:54:54 UTC (rev 239858)
@@ -24,9 +24,31 @@
  */
 
 .sidebar > .panel.navigation.audit > .content {
+    display: flex;
+    flex-direction: column;
     top: var(--navigation-bar-height);
 }
 
+.sidebar > .panel.navigation.audit > .content > .tree-outline {
+    flex-grow: 1;
+}
+
+.sidebar > .panel.navigation.audit > .content .edit-audits:not(.disabled):active {
+    color: var(--glyph-color-pressed);
+}
+
+.sidebar > .panel.navigation.audit > .content .edit-audits:not(.disabled).activated {
+    color: var(--glyph-color-active);
+}
+
+.sidebar > .panel.navigation.audit > .content .edit-audits:not(.disabled).activated:active {
+    color: var(--glyph-color-active-pressed);
+}
+
+.sidebar > .panel.navigation.audit > .content .edit-audits.disabled {
+    color: var(--glyph-color-disabled);
+}
+
 .sidebar > .panel.navigation.audit.has-results:not(.has-tests) > .content > .message-text-view {
     position: initial;
     border-bottom: 1px solid var(--border-color);
@@ -39,3 +61,8 @@
 .sidebar > .panel.navigation.audit.has-results:not(.has-tests) > .content > .message-text-view > button {
     margin: 8px 0 7px;
 }
+
+.finish-editing-audits-placeholder.message-text-view .navigation-item-help .navigation-bar {
+    padding: 0;
+    vertical-align: 0.5px;
+}

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/AuditNavigationSidebarPanel.js (239857 => 239858)


--- trunk/Source/WebInspectorUI/UserInterface/Views/AuditNavigationSidebarPanel.js	2019-01-11 04:46:23 UTC (rev 239857)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/AuditNavigationSidebarPanel.js	2019-01-11 05:54:54 UTC (rev 239858)
@@ -36,16 +36,30 @@
     {
         let contentView = new WI.ContentView;
 
-        let contentPlaceholder = WI.createMessageTextView(WI.UIString("No audit selected"));
-        contentView.element.appendChild(contentPlaceholder);
+        if (WI.auditManager.editing) {
+            let contentPlaceholder = WI.createMessageTextView(WI.UIString("Editing audits"));
+            contentPlaceholder.classList.add("finish-editing-audits-placeholder");
+            contentView.element.appendChild(contentPlaceholder);
 
-        let importNavigationItem = new WI.ButtonNavigationItem("import-audit", WI.UIString("Import"), "Images/Import.svg", 15, 15);
-        importNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText;
-        importNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleImportButtonNavigationItemClicked, this);
+            let finishEditingNavigationItem = new WI.ButtonNavigationItem("finish-editing-audits", WI.UIString("Done"));
+            finishEditingNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, (event) => {
+                WI.auditManager.editing = false;
+            });
 
-        let importHelpElement = WI.createNavigationItemHelp(WI.UIString("Press %s to import a test or result file"), importNavigationItem);
-        contentPlaceholder.appendChild(importHelpElement);
+            let importHelpElement = WI.createNavigationItemHelp(WI.UIString("Press %s to stop editing"), finishEditingNavigationItem);
+            contentPlaceholder.appendChild(importHelpElement);
+        } else {
+            let contentPlaceholder = WI.createMessageTextView(WI.UIString("No audit selected"));
+            contentView.element.appendChild(contentPlaceholder);
 
+             let importNavigationItem = new WI.ButtonNavigationItem("import-audit", WI.UIString("Import"), "Images/Import.svg", 15, 15);
+            importNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText;
+            importNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleImportButtonNavigationItemClicked, this);
+
+             let importHelpElement = WI.createNavigationItemHelp(WI.UIString("Press %s to import a test or result file"), importNavigationItem);
+            contentPlaceholder.appendChild(importHelpElement);
+        }
+
         this.contentBrowser.showContentView(contentView);
     }
 
@@ -57,24 +71,32 @@
 
         this.contentTreeOutline.allowsRepeatSelection = false;
 
-        let navigationBar = new WI.NavigationBar;
+        let controlsNavigationBar = new WI.NavigationBar;
 
         this._startStopButtonNavigationItem = new WI.ToggleButtonNavigationItem("audit-start-stop", WI.UIString("Start"), WI.UIString("Stop"), "Images/AuditStart.svg", "Images/AuditStop.svg", 13, 13);
         this._startStopButtonNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText;
         this._updateStartStopButtonNavigationItemState();
         this._startStopButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleStartStopButtonNavigationItemClicked, this);
-        navigationBar.addNavigationItem(this._startStopButtonNavigationItem);
+        controlsNavigationBar.addNavigationItem(this._startStopButtonNavigationItem);
 
-        navigationBar.addNavigationItem(new WI.DividerNavigationItem);
+        controlsNavigationBar.addNavigationItem(new WI.DividerNavigationItem);
 
         let importButtonNavigationItem = new WI.ButtonNavigationItem("audit-import", WI.UIString("Import"), "Images/Import.svg", 15, 15);
         importButtonNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText;
         importButtonNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.Low;
         importButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleImportButtonNavigationItemClicked, this);
-        navigationBar.addNavigationItem(importButtonNavigationItem);
+        controlsNavigationBar.addNavigationItem(importButtonNavigationItem);
 
-        this.addSubview(navigationBar);
+        this.addSubview(controlsNavigationBar);
 
+        let editNavigationbar = new WI.NavigationBar;
+
+        this._editButtonNavigationItem = new WI.ActivateButtonNavigationItem("edit-audits", WI.UIString("Edit"), WI.UIString("Done"));
+        this._editButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleEditButtonNavigationItemClicked, this);
+        editNavigationbar.addNavigationItem(this._editButtonNavigationItem);
+
+        this.contentView.addSubview(editNavigationbar);
+
         for (let test of WI.auditManager.tests)
             this._addTest(test);
 
@@ -82,6 +104,7 @@
             this._addResult(result, i);
         });
 
+        WI.auditManager.addEventListener(WI.AuditManager.Event.EditingChanged, this._handleAuditManagerEditingChanged, this);
         WI.auditManager.addEventListener(WI.AuditManager.Event.TestAdded, this._handleAuditTestAdded, this);
         WI.auditManager.addEventListener(WI.AuditManager.Event.TestCompleted, this._handleAuditTestCompleted, this);
         WI.auditManager.addEventListener(WI.AuditManager.Event.TestRemoved, this._handleAuditTestRemoved, this);
@@ -105,6 +128,24 @@
             this._updateNoAuditsPlaceholder();
     }
 
+    hasCustomFilters()
+    {
+        return true;
+    }
+
+    matchTreeElementAgainstCustomFilters(treeElement, flags)
+    {
+        if (WI.auditManager.editing) {
+            if (treeElement.representedObject instanceof WI.AuditTestResultBase || treeElement.hasAncestor(this._resultsFolderTreeElement) || treeElement === this._resultsFolderTreeElement)
+                return false;
+        } else {
+            if (treeElement.representedObject instanceof WI.AuditTestBase && treeElement.representedObject.disabled)
+                return false;
+        }
+
+        return super.matchTreeElementAgainstCustomFilters(treeElement, flags);
+    }
+
     // Private
 
     _addTest(test)
@@ -111,8 +152,6 @@
     {
         this.element.classList.add("has-tests");
 
-        this._updateStartStopButtonNavigationItemState();
-
         let treeElement = new WI.AuditTreeElement(test);
 
         if (this._resultsFolderTreeElement) {
@@ -121,6 +160,9 @@
         } else
             this.contentTreeOutline.appendChild(treeElement);
 
+        this._updateStartStopButtonNavigationItemState();
+        this._updateEditButtonNavigationItemState();
+
         this.hideEmptyContentPlaceholder();
     }
 
@@ -128,8 +170,6 @@
     {
         this.element.classList.add("has-results");
 
-        this._updateStartStopButtonNavigationItemState();
-
         if (!this._resultsFolderTreeElement) {
             this._resultsFolderTreeElement = new WI.FolderTreeElement(WI.UIString("Results"));
             this.contentTreeOutline.appendChild(this._resultsFolderTreeElement);
@@ -144,16 +184,28 @@
         }
         this._resultsFolderTreeElement.appendChild(resultFolderTreeElement);
 
+        console.assert(this._resultsFolderTreeElement.children.length === WI.auditManager.results.length);
+
         for (let resultItem of result)
             resultFolderTreeElement.appendChild(new WI.AuditTreeElement(resultItem));
+
+        this._updateStartStopButtonNavigationItemState();
+        this._updateEditButtonNavigationItemState();
     }
 
     _updateStartStopButtonNavigationItemState()
     {
-        this._startStopButtonNavigationItem.toggled = WI.auditManager.runningState !== WI.AuditManager.RunningState.Inactive;
-        this._startStopButtonNavigationItem.enabled = WI.auditManager.tests.length && WI.auditManager.runningState !== WI.AuditManager.RunningState.Stopping;
+        this._startStopButtonNavigationItem.toggled = WI.auditManager.runningState === WI.AuditManager.RunningState.Active || WI.auditManager.runningState === WI.AuditManager.RunningState.Stopping;
+        this._startStopButtonNavigationItem.enabled = WI.auditManager.tests.length && (WI.auditManager.runningState === WI.AuditManager.RunningState.Inactive || WI.auditManager.runningState === WI.AuditManager.RunningState.Active);
     }
 
+     _updateEditButtonNavigationItemState()
+    {
+        this._editButtonNavigationItem.label = WI.auditManager.editing ? this._editButtonNavigationItem.activatedToolTip : this._editButtonNavigationItem.defaultToolTip;
+        this._editButtonNavigationItem.activated = WI.auditManager.editing;
+        this._editButtonNavigationItem.enabled = WI.auditManager.tests.length && (WI.auditManager.editing || WI.auditManager.runningState === WI.AuditManager.RunningState.Inactive);
+    }
+
     _updateNoAuditsPlaceholder()
     {
         if (WI.auditManager.tests.length)
@@ -176,8 +228,32 @@
             // be styled such that only the button is visible.
             this.contentView.element.insertBefore(contentPlaceholder, this.contentView.element.firstChild);
         }
+
+        this._updateEditButtonNavigationItemState();
     }
 
+    _handleAuditManagerEditingChanged(event)
+    {
+        if (WI.auditManager.editing) {
+            console.assert(!this._selectedTreeElementBeforeEditing);
+            this._selectedTreeElementBeforeEditing = this.contentTreeOutline.selectedTreeElement;
+            if (this._selectedTreeElementBeforeEditing)
+                this._selectedTreeElementBeforeEditing.deselect();
+        } else if (this._selectedTreeElementBeforeEditing) {
+            if (!(this._selectedTreeElementBeforeEditing.representedObject instanceof WI.AuditTestBase) || !this._selectedTreeElementBeforeEditing.representedObject.disabled)
+                this._selectedTreeElementBeforeEditing.select();
+            this._selectedTreeElementBeforeEditing = null;
+        }
+
+        if (!this.contentTreeOutline.selectedTreeElement)
+            this.showDefaultContentView();
+
+        this._updateStartStopButtonNavigationItemState();
+        this._updateEditButtonNavigationItemState();
+
+        this.updateFilter();
+    }
+
     _handleAuditTestAdded(event)
     {
         this._addTest(event.data.test);
@@ -204,6 +280,7 @@
     _handleAuditTestScheduled(event)
     {
         this._updateStartStopButtonNavigationItemState();
+        this._updateEditButtonNavigationItemState();
     }
 
     _treeSelectionDidChange(event)
@@ -217,6 +294,10 @@
             return;
         }
 
+        console.assert(!WI.auditManager.editing);
+        if (WI.auditManager.editing)
+            return;
+
         let representedObject = treeElement.representedObject;
         if (representedObject instanceof WI.AuditTestCase || representedObject instanceof WI.AuditTestGroup
             || representedObject instanceof WI.AuditTestCaseResult || representedObject instanceof WI.AuditTestGroupResult) {
@@ -241,4 +322,9 @@
     {
         WI.FileUtilities.importJSON((result) => WI.auditManager.processJSON(result));
     }
+
+    _handleEditButtonNavigationItemClicked(event)
+    {
+        WI.auditManager.editing = !WI.auditManager.editing;
+    }
 };

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/AuditTestGroupContentView.js (239857 => 239858)


--- trunk/Source/WebInspectorUI/UserInterface/Views/AuditTestGroupContentView.js	2019-01-11 04:46:23 UTC (rev 239857)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/AuditTestGroupContentView.js	2019-01-11 05:54:54 UTC (rev 239858)
@@ -149,6 +149,9 @@
         }
 
         for (let subobject of this._subobjects()) {
+            if (subobject instanceof WI.AuditTestBase && subobject.disabled)
+                continue;
+
             let view = WI.ContentView.contentViewForRepresentedObject(subobject);
             this.contentView.addSubview(view);
             view.shown();

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/AuditTreeElement.css (239857 => 239858)


--- trunk/Source/WebInspectorUI/UserInterface/Views/AuditTreeElement.css	2019-01-11 04:46:23 UTC (rev 239857)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/AuditTreeElement.css	2019-01-11 05:54:54 UTC (rev 239858)
@@ -43,12 +43,12 @@
 }
 
 .tree-outline .item.audit > .status:not(:hover) > img.show-on-hover,
-.tree-outline .item.audit.test-group.expanded > .status:not(:hover) {
+.tree-outline .item.audit.test-group.expanded:not(.editing-audits) > .status:not(:hover) {
     opacity: 0;
 }
 
 .tree-outline .item.audit.manager-active > .status > img.show-on-hover,
-.tree-outline .item.audit.test-group.expanded > .status:hover > :not(img),
+.tree-outline .item.audit.test-group.expanded:not(.editing-audits) > .status:hover > :not(img),
 .tree-outline .item.audit.test-group-result.expanded > .status {
     display: none;
 }

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/AuditTreeElement.js (239857 => 239858)


--- trunk/Source/WebInspectorUI/UserInterface/Views/AuditTreeElement.js	2019-01-11 04:46:23 UTC (rev 239857)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/AuditTreeElement.js	2019-01-11 05:54:54 UTC (rev 239858)
@@ -61,6 +61,7 @@
         super.onattach();
 
         if (this.representedObject instanceof WI.AuditTestBase) {
+            this.representedObject.addEventListener(WI.AuditTestBase.Event.DisabledChanged, this._handleTestDisabledChanged, this);
             this.representedObject.addEventListener(WI.AuditTestBase.Event.ResultCleared, this._handleTestResultCleared, this);
 
             if (this.representedObject instanceof WI.AuditTestCase)
@@ -68,6 +69,7 @@
             else if (this.representedObject instanceof WI.AuditTestGroup)
                 this.representedObject.addEventListener(WI.AuditTestBase.Event.Scheduled, this._handleTestGroupScheduled, this);
 
+            WI.auditManager.addEventListener(WI.AuditManager.Event.EditingChanged, this._handleManagerEditingChanged, this);
             WI.auditManager.addEventListener(WI.AuditManager.Event.TestScheduled, this._handleAuditManagerTestScheduled, this);
             WI.auditManager.addEventListener(WI.AuditManager.Event.TestCompleted, this._handleAuditManagerTestCompleted, this);
         }
@@ -162,6 +164,11 @@
         super.populateContextMenu(contextMenu, event);
     }
 
+    canSelectOnMouseDown(event)
+    {
+        return !WI.auditManager.editing;
+    }
+
     // Private
 
     _start()
@@ -232,6 +239,14 @@
         this.status.value = progress || 0;
     }
 
+    _updateTestGroupDisabled()
+    {
+        this.status.checked = !this.representedObject.disabled;
+
+        if (this.representedObject instanceof WI.AuditTestGroup)
+            this.status.indeterminate = this.representedObject.tests.some((test) => test.disabled !== this.representedObject.tests[0].disabled);
+    }
+
     _handleTestCaseCompleted(event)
     {
         this.representedObject.removeEventListener(WI.AuditTestBase.Event.Completed, this._handleTestCaseCompleted, this);
@@ -239,6 +254,12 @@
         this._updateLevel();
     }
 
+    _handleTestDisabledChanged(event)
+    {
+        if (this.status instanceof HTMLInputElement && this.status.type === "checkbox")
+            this._updateTestGroupDisabled();
+    }
+
     _handleTestResultCleared(event)
     {
         this._updateLevel();
@@ -273,6 +294,24 @@
         this._showRunningProgress();
     }
 
+    _handleManagerEditingChanged(event)
+    {
+        if (WI.auditManager.editing) {
+            this.status = document.createElement("input");
+            this.status.type = "checkbox";
+            this._updateTestGroupDisabled();
+            this.status.addEventListener("change", () => {
+                this.representedObject.disabled = !this.representedObject.disabled;
+            });
+
+            this.addClassName("editing-audits");
+        } else {
+            this.removeClassName("editing-audits");
+
+            this._updateLevel();
+        }
+    }
+
     _handleAuditManagerTestScheduled(event)
     {
         this.addClassName("manager-active");

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TreeOutline.css (239857 => 239858)


--- trunk/Source/WebInspectorUI/UserInterface/Views/TreeOutline.css	2019-01-11 04:46:23 UTC (rev 239857)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TreeOutline.css	2019-01-11 05:54:54 UTC (rev 239858)
@@ -40,7 +40,7 @@
     display: none;
 }
 
-.tree-outline .children.expanded {
+.tree-outline .children.expanded:not([hidden]) {
     display: block;
 }
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to