Title: [199789] trunk
Revision
199789
Author
[email protected]
Date
2016-04-20 14:58:44 -0700 (Wed, 20 Apr 2016)

Log Message

Web Inspector: Make debounce use an ES6 Proxy

https://bugs.webkit.org/show_bug.cgi?id=156756
rdar://problem/25809771

Reviewed by Joseph Pecoraro.

Source/WebInspectorUI:

* UserInterface/Base/Utilities.js:
(Object.prototype.soon): Added.
(Object.prototype.debounce): Added.
(Function.prototype.debounce): Deleted.
(Function.prototype.cancelDebounce): Added.

* UserInterface/Views/BezierEditor.js:
(WebInspector.BezierEditor.createBezierInput): Use new debounce proxy.
* UserInterface/Views/VisualStyleBackgroundPicker.js:
(WebInspector.VisualStyleBackgroundPicker): Ditto.
* UserInterface/Views/VisualStyleURLInput.js:
(WebInspector.VisualStyleURLInput): Ditto.

LayoutTests:

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

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (199788 => 199789)


--- trunk/LayoutTests/ChangeLog	2016-04-20 21:54:09 UTC (rev 199788)
+++ trunk/LayoutTests/ChangeLog	2016-04-20 21:58:44 UTC (rev 199789)
@@ -1,3 +1,15 @@
+2016-04-19  Timothy Hatcher  <[email protected]>
+
+        Web Inspector: Make debounce use an ES6 Proxy
+
+        https://bugs.webkit.org/show_bug.cgi?id=156756
+        rdar://problem/25809771
+
+        Reviewed by Joseph Pecoraro.
+
+        * inspector/unit-tests/debounce-expected.txt: Added.
+        * inspector/unit-tests/debounce.html: Added.
+
 2016-04-20  Ryan Haddad  <[email protected]>
 
         Rebaseline tests for iOS simulator.

Added: trunk/LayoutTests/inspector/unit-tests/debounce-expected.txt (0 => 199789)


--- trunk/LayoutTests/inspector/unit-tests/debounce-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/inspector/unit-tests/debounce-expected.txt	2016-04-20 21:58:44 UTC (rev 199789)
@@ -0,0 +1,30 @@
+Testing debounce proxy support.
+
+
+== Running test suite: Debouce
+-- Running test case: Basic Debounce
+PASS: Debounced successfully.
+PASS: Call delayed at least 100ms.
+PASS: `this` is the right object.
+PASS: Arguments length is 2.
+PASS: First argument is 4.
+PASS: Second argument is 'abc'.
+
+-- Running test case: Multiple Debounce Delays
+PASS: Debounced successfully.
+PASS: Call delayed at least 400ms.
+PASS: `this` is the right object.
+PASS: Arguments length is 2.
+PASS: First argument is 4.
+PASS: Second argument is 'abc'.
+
+-- Running test case: Soon Debounce
+PASS: Debounced successfully.
+PASS: `this` is the right object.
+PASS: Arguments length is 2.
+PASS: First argument is 4.
+PASS: Second argument is 'abc'.
+
+-- Running test case: Cancel Debounce
+PASS: Debounced function canceled.
+

Added: trunk/LayoutTests/inspector/unit-tests/debounce.html (0 => 199789)


--- trunk/LayoutTests/inspector/unit-tests/debounce.html	                        (rev 0)
+++ trunk/LayoutTests/inspector/unit-tests/debounce.html	2016-04-20 21:58:44 UTC (rev 199789)
@@ -0,0 +1,152 @@
+<!doctype html>
+<html>
+<head>
+<script src=""
+<script>
+function test()
+{
+    let suite = InspectorTest.createAsyncSuite("Debouce");
+
+    suite.addTestCase({
+        name: "Basic Debounce",
+        test: function(resolve, reject) {
+            let callCount = 0;
+            let startTime = performance.now();
+
+            let object = {
+                test(foo, bar) {
+                    if (++callCount === 1)
+                        InspectorTest.pass("Debounced successfully.");
+                    else
+                        InspectorTest.fail("Debounced function called multiple times.");
+
+                    InspectorTest.expectThat(performance.now() - startTime >= 100, "Call delayed at least 100ms.");
+
+                    InspectorTest.expectThat(this === object, "`this` is the right object.");
+                    InspectorTest.expectThat(arguments.length === 2, "Arguments length is 2.");
+                    InspectorTest.expectThat(foo === 4, "First argument is 4.");
+                    InspectorTest.expectThat(bar === "abc", "Second argument is 'abc'.");
+
+                    resolve();
+                }
+            };
+
+            let debounceProxy = object.debounce(100);
+            debounceProxy.test(1, 'xyz');
+            debounceProxy.test(2, 'fgh');
+            debounceProxy.test(3, 'ert');
+            debounceProxy.test(4, 'abc');
+
+            if (!callCount)
+                return;
+
+            InspectorTest.fail("Debounced function called immediately.");
+            resolve();
+        }
+    });
+
+    suite.addTestCase({
+        name: "Multiple Debounce Delays",
+        test: function(resolve, reject) {
+            let callCount = 0;
+            let startTime = performance.now();
+
+            let object = {
+                test(foo, bar) {
+                    if (++callCount === 1)
+                        InspectorTest.pass("Debounced successfully.");
+                    else
+                        InspectorTest.fail("Debounced function called multiple times.");
+
+                    InspectorTest.expectThat(performance.now() - startTime >= 400, "Call delayed at least 400ms.");
+
+                    InspectorTest.expectThat(this === object, "`this` is the right object.");
+                    InspectorTest.expectThat(arguments.length === 2, "Arguments length is 2.");
+                    InspectorTest.expectThat(foo === 4, "First argument is 4.");
+                    InspectorTest.expectThat(bar === "abc", "Second argument is 'abc'.");
+
+                    resolve();
+                }
+            };
+
+            object.debounce(100).test(1, 'xyz');
+            object.debounce(200).test(2, 'fgh');
+            object.debounce(300).test(3, 'ert');
+            object.debounce(400).test(4, 'abc');
+
+            if (!callCount)
+                return;
+
+            InspectorTest.fail("Debounced function called immediately.");
+            resolve();
+        }
+    });
+
+    suite.addTestCase({
+        name: "Soon Debounce",
+        test: function(resolve, reject) {
+            let callCount = 0;
+
+            let object = {
+                test(foo, bar) {
+                    if (++callCount === 1)
+                        InspectorTest.pass("Debounced successfully.");
+                    else
+                        InspectorTest.fail("Debounced function called multiple times.");
+
+                    InspectorTest.expectThat(this === object, "`this` is the right object.");
+                    InspectorTest.expectThat(arguments.length === 2, "Arguments length is 2.");
+                    InspectorTest.expectThat(foo === 4, "First argument is 4.");
+                    InspectorTest.expectThat(bar === "abc", "Second argument is 'abc'.");
+
+                    resolve();
+                }
+            };
+
+            object.soon.test(1, 'xyz');
+            object.soon.test(2, 'fgh');
+            object.soon.test(3, 'ert');
+            object.soon.test(4, 'abc');
+
+            if (!callCount)
+                return;
+
+            InspectorTest.fail("Debounced function called immediately.");
+            resolve();
+        }
+    });
+
+    suite.addTestCase({
+        name: "Cancel Debounce",
+        test: function(resolve, reject) {
+            let callCount = 0;
+            let startTime = performance.now();
+
+            let object = {
+                test(foo, bar) {
+                    InspectorTest.fail("Debounced function call should have been canceled.");
+                    resolve();
+                }
+            };
+
+            object.debounce(100).test(1, 'xyz');
+            object.test.cancelDebounce();
+
+            setTimeout(() => { InspectorTest.pass("Debounced function canceled."); resolve(); }, 200);
+
+            if (!callCount)
+                return;
+
+            InspectorTest.fail("Debounced function called immediately.");
+            resolve();
+        }
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body _onload_="runTest()">
+    <p>Testing debounce proxy support.</p>
+</body>
+</html>

Modified: trunk/Source/WebInspectorUI/ChangeLog (199788 => 199789)


--- trunk/Source/WebInspectorUI/ChangeLog	2016-04-20 21:54:09 UTC (rev 199788)
+++ trunk/Source/WebInspectorUI/ChangeLog	2016-04-20 21:58:44 UTC (rev 199789)
@@ -1,3 +1,25 @@
+2016-04-19  Timothy Hatcher  <[email protected]>
+
+        Web Inspector: Make debounce use an ES6 Proxy
+
+        https://bugs.webkit.org/show_bug.cgi?id=156756
+        rdar://problem/25809771
+
+        Reviewed by Joseph Pecoraro.
+
+        * UserInterface/Base/Utilities.js:
+        (Object.prototype.soon): Added.
+        (Object.prototype.debounce): Added.
+        (Function.prototype.debounce): Deleted.
+        (Function.prototype.cancelDebounce): Added.
+
+        * UserInterface/Views/BezierEditor.js:
+        (WebInspector.BezierEditor.createBezierInput): Use new debounce proxy.
+        * UserInterface/Views/VisualStyleBackgroundPicker.js:
+        (WebInspector.VisualStyleBackgroundPicker): Ditto.
+        * UserInterface/Views/VisualStyleURLInput.js:
+        (WebInspector.VisualStyleURLInput): Ditto.
+
 2016-04-19  Joseph Pecoraro  <[email protected]>
 
         Web Inspector: Picking Snapshot from navigation bar popup does to switch views

Modified: trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js (199788 => 199789)


--- trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js	2016-04-20 21:54:09 UTC (rev 199788)
+++ trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js	2016-04-20 21:58:44 UTC (rev 199789)
@@ -1194,27 +1194,64 @@
 });
 
 (function() {
-    // The `debounce` function lets you call a function with a delay and
-    // if the function keeps getting called, the delay gets reset.
-    // Note: The last call's arguments end being the ones that get used.
-    // Use: foo.bar.debounce(200, foo)("Argument 1", "Argument 2")
+    // The `debounce` function lets you call any function on an object with a delay
+    // and if the function keeps getting called, the delay gets reset. Since `debounce`
+    // returns a Proxy, you can cache it and call multiple functions with the same delay.
 
-    const debounceSymbol = Symbol("function-debounce-timeout");
+    // Use: object.debounce(200).foo("Argument 1", "Argument 2")
+    // Note: The last call's arguments get used for the delayed call.
 
-    Object.defineProperty(Function.prototype, "debounce",
+    const debounceTimeoutSymbol = Symbol("debounce-timeout");
+    const debounceSoonProxySymbol = Symbol("debounce-soon-proxy");
+
+    Object.defineProperty(Object.prototype, "soon",
     {
-        value: function(delay, thisObject)
+        get: function()
         {
-            return () => {
-                clearTimeout(this[debounceSymbol]);
+            if (!this[debounceSoonProxySymbol])
+                this[debounceSoonProxySymbol] = this.debounce(0);
+            return this[debounceSoonProxySymbol];
+        }
+    });
 
-                let args = arguments;
-                this[debounceSymbol] = setTimeout(() => {
-                    this.apply(thisObject, args);
-                }, delay);
-            };
+    Object.defineProperty(Object.prototype, "debounce",
+    {
+        value: function(delay)
+        {
+            console.assert(delay >= 0);
+
+            return new Proxy(this, {
+                get(target, property, receiver) {
+                    return (...args) => {
+                        let original = target[property];
+                        console.assert(typeof original === "function");
+
+                        if (original[debounceTimeoutSymbol])
+                            clearTimeout(original[debounceTimeoutSymbol]);
+
+                        let performWork = () => {
+                            original[debounceTimeoutSymbol] = undefined;
+                            original.apply(target, args);
+                        };
+
+                        original[debounceTimeoutSymbol] = setTimeout(performWork, delay);
+                    };
+                }
+            });
         }
     });
+
+    Object.defineProperty(Function.prototype, "cancelDebounce",
+    {
+        value: function()
+        {
+            if (!this[debounceTimeoutSymbol])
+                return;
+
+            clearTimeout(this[debounceTimeoutSymbol]);
+            this[debounceTimeoutSymbol] = undefined;
+        }
+    });
 })();
 
 function appendWebInspectorSourceURL(string)

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/BezierEditor.js (199788 => 199789)


--- trunk/Source/WebInspectorUI/UserInterface/Views/BezierEditor.js	2016-04-20 21:54:09 UTC (rev 199788)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/BezierEditor.js	2016-04-20 21:58:44 UTC (rev 199789)
@@ -92,7 +92,7 @@
         {
             let key = "_bezier" + id + "Input";
             this[key] = this._numberInputContainer.createChild("input", className);
-            this[key].addEventListener("input", this._handleNumberInputInput.debounce(250, this));
+            this[key].addEventListener("input", this.debounce(250)._handleNumberInputInput);
             this[key].addEventListener("keydown", this._handleNumberInputKeydown.bind(this));
         }
 

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/VisualStyleBackgroundPicker.js (199788 => 199789)


--- trunk/Source/WebInspectorUI/UserInterface/Views/VisualStyleBackgroundPicker.js	2016-04-20 21:54:09 UTC (rev 199788)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/VisualStyleBackgroundPicker.js	2016-04-20 21:58:44 UTC (rev 199789)
@@ -37,7 +37,7 @@
         this._valueInputElement.classList.add("value-input");
         this._valueInputElement.type = "url";
         this._valueInputElement.placeholder = WebInspector.UIString("Enter a URL");
-        this._valueInputElement.addEventListener("input", this._valueInputValueChanged.debounce(250, this));
+        this._valueInputElement.addEventListener("input", this.debounce(250)._valueInputValueChanged);
         this.contentElement.appendChild(this._valueInputElement);
 
         this._valueTypePickerElement = document.createElement("select");

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/VisualStyleURLInput.js (199788 => 199789)


--- trunk/Source/WebInspectorUI/UserInterface/Views/VisualStyleURLInput.js	2016-04-20 21:54:09 UTC (rev 199788)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/VisualStyleURLInput.js	2016-04-20 21:58:44 UTC (rev 199789)
@@ -32,7 +32,7 @@
         this._urlInputElement = document.createElement("input");
         this._urlInputElement.type = "url";
         this._urlInputElement.placeholder = WebInspector.UIString("Enter a URL");
-        this._urlInputElement.addEventListener("keyup", this._valueDidChange.debounce(250, this));
+        this._urlInputElement.addEventListener("keyup", this.debounce(250)._valueDidChange);
         this.contentElement.appendChild(this._urlInputElement);
     }
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to