Title: [264853] trunk
Revision
264853
Author
[email protected]
Date
2020-07-24 13:48:42 -0700 (Fri, 24 Jul 2020)

Log Message

Propagating user gesture through Fetch API
https://bugs.webkit.org/show_bug.cgi?id=214444
<rdar://problem/65980953>

Reviewed by Youenn Fablet.

Source/WebCore:

This patch propagates media only user gestures through Fetch API, including Fetch and FetchResponse.

Covered by new test contents.

* Modules/fetch/DOMWindowFetch.cpp:
(WebCore::DOMWindowFetch::fetch):
Propagates media only user gestures through Fetch.

* Modules/fetch/FetchBodyConsumer.cpp:
(WebCore::FetchBodyConsumer::clean):
(WebCore::FetchBodyConsumer::resolve):
(WebCore::FetchBodyConsumer::setConsumePromise):
(WebCore::FetchBodyConsumer::resetConsumePromise):
(WebCore::FetchBodyConsumer::loadingFailed):
(WebCore::FetchBodyConsumer::loadingSucceeded):
* Modules/fetch/FetchBodyConsumer.h:
(WebCore::FetchBodyConsumer::setMaximumIntervalForUserGestureForwarding):
(WebCore::FetchBodyConsumer::FetchBodyConsumer):
Propagates media only user gestures through FetchResponse. UserGestureIndicator is only set in the async promise resolving path.

* WebCore.xcodeproj/project.pbxproj:
* bindings/js/JSDOMWindowBase.cpp:
(WebCore::JSDOMWindowBase::queueMicrotaskToEventLoop):
Propagates media only user gestures that was propagated through Fetch to the related promise executors.

* dom/UserGestureIndicator.cpp:
(WebCore::UserGestureIndicator::UserGestureIndicator):
(WebCore::UserGestureIndicator::~UserGestureIndicator):
* dom/UserGestureIndicator.h:
(WebCore::UserGestureToken::setIsPropagatedFromFetch):
(WebCore::UserGestureToken::resetIsPropagatedFromFetch):
(WebCore::UserGestureToken::isPropagatedFromFetch const):
Adds a flag to indicate the propagation source of a user gesture token. And makes pipelines from testing.

* page/DOMTimer.cpp:
(WebCore::DOMTimer::fired):
Gathers some constants.

* testing/Internals.cpp:
(WebCore::Internals::setFetchMaximumIntervalForUserGestureForwarding):
* testing/Internals.h:
* testing/Internals.idl:
Crafts a way to pass the maximum interval from layout tests.

* xml/XMLHttpRequest.cpp:
(WebCore::XMLHttpRequest::XMLHttpRequest):
Gathers some constants.

LayoutTests:

* http/tests/media/user-gesture-preserved-across-xmlhttprequest-expected.txt:
* http/tests/media/user-gesture-preserved-across-xmlhttprequest.html:
* http/tests/xmlhttprequest/resources/download-header-with-delay.php: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (264852 => 264853)


--- trunk/LayoutTests/ChangeLog	2020-07-24 19:59:56 UTC (rev 264852)
+++ trunk/LayoutTests/ChangeLog	2020-07-24 20:48:42 UTC (rev 264853)
@@ -1,3 +1,15 @@
+2020-07-24  Jiewen Tan  <[email protected]>
+
+        Propagating user gesture through Fetch API
+        https://bugs.webkit.org/show_bug.cgi?id=214444
+        <rdar://problem/65980953>
+
+        Reviewed by Youenn Fablet.
+
+        * http/tests/media/user-gesture-preserved-across-xmlhttprequest-expected.txt:
+        * http/tests/media/user-gesture-preserved-across-xmlhttprequest.html:
+        * http/tests/xmlhttprequest/resources/download-header-with-delay.php: Added.
+
 2020-07-24  Karl Rackler  <[email protected]>
 
         [ iOS wk2 ] imported/w3c/web-platform-tests/css/css-fonts/crash-font-face-invalid-descriptor.html is a flaky timeout

Modified: trunk/LayoutTests/http/tests/media/user-gesture-preserved-across-xmlhttprequest-expected.txt (264852 => 264853)


--- trunk/LayoutTests/http/tests/media/user-gesture-preserved-across-xmlhttprequest-expected.txt	2020-07-24 19:59:56 UTC (rev 264852)
+++ trunk/LayoutTests/http/tests/media/user-gesture-preserved-across-xmlhttprequest-expected.txt	2020-07-24 20:48:42 UTC (rev 264853)
@@ -52,5 +52,91 @@
 RUN(shouldReject(mediaElement.play()).then(testComplete, testComplete))
 Promise rejected correctly OK
 
+** gesture -> fetch -> window.open: should fail because Fetch only propagates user gesture for media
+fetching, delay = 100
+FETCH(resolved): status = 200
+EXPECTED (window.open("about:blank") == 'null') OK
+
+** gesture -> fetch -> video playback: should succeed
+EVENT(canplaythrough)
+fetching, delay = 100
+FETCH(resolved): status = 200
+RUN(shouldResolve(mediaElement.play()).then(testComplete, testComplete))
+Promise resolved OK
+
+** gesture -> "long" Fetch -> video playback: should fail
+EVENT(canplaythrough)
+fetching, delay = 300
+FETCH(resolved): status = 200
+RUN(shouldReject(mediaElement.play()).then(testComplete, testComplete))
+Promise rejected correctly OK
+
+** no gesture -> fetch -> video playback: should fail
+EVENT(canplaythrough)
+fetching, delay = 100
+FETCH(resolved): status = 200
+RUN(shouldReject(mediaElement.play()).then(testComplete, testComplete))
+Promise rejected correctly OK
+
+** gesture -> fetch -> blob -> window.open: should fail because Fetch only propagates user gesture for media
+fetching blob, delay = 100
+FETCH(resolved): status = 200
+FETCH(response resolved)
+EXPECTED (window.open("about:blank") == 'null') OK
+
+** gesture -> fetch -> blob -> video playback: should succeed
+EVENT(canplaythrough)
+fetching blob, delay = 100
+FETCH(resolved): status = 200
+FETCH(response resolved)
+RUN(shouldResolve(mediaElement.play()).then(testComplete, testComplete))
+Promise resolved OK
+
+** gesture -> fetch -> "long" blob -> video playback: should fail
+EVENT(canplaythrough)
+fetching blob, delay = 300
+FETCH(resolved): status = 200
+FETCH(response resolved)
+RUN(shouldReject(mediaElement.play()).then(testComplete, testComplete))
+Promise rejected correctly OK
+
+** no gesture -> fetch -> blob -> video playback: should fail
+EVENT(canplaythrough)
+fetching blob, delay = 100
+FETCH(resolved): status = 200
+FETCH(response resolved)
+RUN(shouldReject(mediaElement.play()).then(testComplete, testComplete))
+Promise rejected correctly OK
+
+** no gesture -> promise -> video playback: should fail
+EVENT(canplaythrough)
+promise
+PROMISE(resolved)
+RUN(shouldReject(mediaElement.play()).then(testComplete, testComplete))
+Promise rejected correctly OK
+
+** gesture -> promise -> video playback: should fail because promise only propagates user gesture propagated through Fetch
+EVENT(canplaythrough)
+promise
+PROMISE(resolved)
+RUN(shouldReject(mediaElement.play()).then(testComplete, testComplete))
+Promise rejected correctly OK
+
+** gesture -> fetch -> promise -> video playback: should succeed
+EVENT(canplaythrough)
+fetch then promise
+FETCH(resolved): status = 200
+PROMISE(resolved)
+RUN(shouldResolve(mediaElement.play()).then(testComplete, testComplete))
+Promise resolved OK
+
+** gesture -> fetch -> crypto -> video playback: should fail since the user gesture will not be propagated through other API even though it returns promises
+EVENT(canplaythrough)
+fetch then crypto
+FETCH(resolved): status = 200
+CRYPTO(key generated)
+RUN(shouldReject(mediaElement.play()).then(testComplete, testComplete))
+Promise rejected correctly OK
+
 END OF TEST
 

Modified: trunk/LayoutTests/http/tests/media/user-gesture-preserved-across-xmlhttprequest.html (264852 => 264853)


--- trunk/LayoutTests/http/tests/media/user-gesture-preserved-across-xmlhttprequest.html	2020-07-24 19:59:56 UTC (rev 264852)
+++ trunk/LayoutTests/http/tests/media/user-gesture-preserved-across-xmlhttprequest.html	2020-07-24 20:48:42 UTC (rev 264853)
@@ -19,7 +19,7 @@
         {
             var xhr = new XMLHttpRequest();
             if (window.internals)
-                window.internals.setXHRMaximumIntervalForUserGestureForwarding(xhr, timeout);
+                window.internals.setMaximumIntervalForUserGestureForwardingForFetch(timeout);
 
             xhr.open('GET', `/xmlhttprequest/resources/download-with-delay.php?iteration=1&delay=${delay}`, true);
 
@@ -41,6 +41,70 @@
             xhr.send(null);
         }
 
+        function doFetch(delay, timeout, completionHandler)
+        {
+            if (window.internals)
+                window.internals.setMaximumIntervalForUserGestureForwardingForFetch(timeout);
+
+            fetch(`/xmlhttprequest/resources/download-header-with-delay.php?delay=${delay}`, { method: 'GET', cache: "no-cache" }).then(response => {
+                consoleWrite(`FETCH(resolved): status = ${response.status}`);
+                completionHandler();
+            }, error => {
+                consoleWrite(`FETCH(rejected): exception code = ${error.code}`);
+            });
+        }
+
+        function doFetchBlob(delay, timeout, completionHandler)
+        {
+            fetch(`/xmlhttprequest/resources/download-with-delay.php?iteration=1&delay=${delay}`, { method: 'GET', cache: "no-cache" }).then(response => {
+                consoleWrite(`FETCH(resolved): status = ${response.status}`);
+                if (window.internals)
+                    window.internals.setMaximumIntervalForUserGestureForwardingForFetch(timeout);
+                return response.blob();
+            }, error => {
+                consoleWrite(`FETCH(rejected): exception code = ${error.code}`);
+            }).then(response => {
+                consoleWrite(`FETCH(response resolved)`);
+                completionHandler();
+            }, error => {
+                consoleWrite(`FETCH(response rejected): exception code = ${error.code}`);
+            });
+        }
+
+        function doPromise(completionHandler)
+        {
+            const promise = new Promise( (resolutionFunc) => {
+                resolutionFunc();
+            });
+
+            promise.then(() => {
+                consoleWrite(`PROMISE(resolved)`);
+                completionHandler()
+            });
+        }
+
+        function doFetchThenPromise(completionHandler)
+        {
+            fetch(`/xmlhttprequest/resources/download-with-delay.php?iteration=1`, { method: 'GET', cache: "no-cache" }).then(response => {
+                consoleWrite(`FETCH(resolved): status = ${response.status}`);
+                return new Promise( (resolutionFunc) => resolutionFunc() );
+            }).then(() => {
+                consoleWrite(`PROMISE(resolved)`);
+                completionHandler()
+            });
+        }
+
+        function doFetchThenCrypto(completionHandler)
+        {
+            fetch(`/xmlhttprequest/resources/download-with-delay.php?iteration=1`, { method: 'GET', cache: "no-cache" }).then(response => {
+                consoleWrite(`FETCH(resolved): status = ${response.status}`);
+                return crypto.subtle.generateKey({name: "RSA-OAEP", modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: "sha-1"}, false, ["decrypt", "encrypt", "wrapKey", "unwrapKey"]);
+            }).then(key => {
+                consoleWrite(`CRYPTO(key generated)`);
+                completionHandler()
+            });
+        }
+
         function testComplete()
         {
             if (autoRun)
@@ -81,8 +145,27 @@
                 consoleWrite(`setting timeout, delay = ${delay}`);
                 setTimeout(() => { runTest(test); }, delay);
                 break;
+            case 'fetch':
+                consoleWrite(`fetching, delay = ${delay}`);
+                doFetch(delay, test.timeout || 0.6, () => runTest(test));
+                break;
+            case 'fetchblob':
+                consoleWrite(`fetching blob, delay = ${delay}`);
+                doFetchBlob(delay, test.timeout || 0.6, () => runTest(test));
+                break;
+            case 'promise':
+                consoleWrite(`promise`);
+                doPromise(() => runTest(test));
+                break;
+            case 'fetchthenpromise':
+                consoleWrite(`fetch then promise`);
+                doFetchThenPromise(() => runTest(test));
+                break;
+            case 'fetchthencrypto':
+                consoleWrite(`fetch then crypto`);
+                doFetchThenCrypto(() => runTest(test));
+                break;
             }
-
         }
 
         let tests = [
@@ -102,7 +185,44 @@
                 action : 'play',  withkey : false,  success : false,  sequence : ['100 XHR', '100 timeout'] },
 
             { title : 'gesture -> "long" XHR -> video playback: should fail',
-                action : 'play',  withkey : false,  success : false,  timeout : 0.1, sequence : ['300 XHR'] },
+                action : 'play',  withkey : true,  success : false,  timeout : 0.1, sequence : ['300 XHR'] },
+
+            { title : 'gesture -> fetch -> window.open: should fail because Fetch only propagates user gesture for media',
+                action : 'popup', withkey : true,  success : false,  sequence : ['100 Fetch'] },
+
+            { title : 'gesture -> fetch -> video playback: should succeed',
+                action : 'play',  withkey : true,  success : true,  sequence : ['100 Fetch'] },
+
+            { title : 'gesture -> "long" Fetch -> video playback: should fail',
+                action : 'play',  withkey : true,  success : false,  timeout : 0.1, sequence : ['300 Fetch'] },
+
+            { title : 'no gesture -> fetch -> video playback: should fail',
+                action : 'play',  withkey : false,  success : false,  sequence : ['100 Fetch'] },
+
+            { title : 'gesture -> fetch -> blob -> window.open: should fail because Fetch only propagates user gesture for media',
+                action : 'popup', withkey : true,  success : false,  sequence : ['100 FetchBlob'] },
+
+            { title : 'gesture -> fetch -> blob -> video playback: should succeed',
+                action : 'play',  withkey : true,  success : true,  sequence : ['100 FetchBlob'] },
+
+            { title : 'gesture -> fetch -> "long" blob -> video playback: should fail',
+                action : 'play',  withkey : true,  success : false,  timeout : 0.1, sequence : ['300 FetchBlob'] },
+
+            { title : 'no gesture -> fetch -> blob -> video playback: should fail',
+                action : 'play',  withkey : false,  success : false,  sequence : ['100 FetchBlob'] },
+
+            { title : 'no gesture -> promise -> video playback: should fail',
+                action : 'play',  withkey : false,  success : false,  sequence : ['0 promise'] },
+
+            { title : 'gesture -> promise -> video playback: should fail because promise only propagates user gesture propagated through Fetch',
+                action : 'play',  withkey : true,  success : false,  sequence : ['0 promise'] },
+
+            { title : 'gesture -> fetch -> promise -> video playback: should succeed',
+                action : 'play',  withkey : true,  success : true,  sequence : ['0 FetchThenPromise'] },
+
+            { title : 'gesture -> fetch -> crypto -> video playback: should fail since the user gesture will not be propagated through other API even though it returns promises',
+                action : 'play',  withkey : true,  success : false,  sequence : ['0 FetchThenCrypto'] },
+
         ];
 
         function nextTest()
@@ -109,7 +229,7 @@
         {
             if (!tests.length) {
                 consoleWrite('');
-                endTest()
+                endTest();
                 return;
             }
 

Added: trunk/LayoutTests/http/tests/xmlhttprequest/resources/download-header-with-delay.php (0 => 264853)


--- trunk/LayoutTests/http/tests/xmlhttprequest/resources/download-header-with-delay.php	                        (rev 0)
+++ trunk/LayoutTests/http/tests/xmlhttprequest/resources/download-header-with-delay.php	2020-07-24 20:48:42 UTC (rev 264853)
@@ -0,0 +1,14 @@
+<?php
+$delay = $_GET['delay'];
+usleep($delay * 1000);
+
+header("Expires: Thu, 01 Dec 2003 16:00:00 GMT");
+header("Cache-Control: no-cache, no-store, must-revalidate");
+header("Pragma: no-cache");
+header("Content-Type: application/x-no-buffering-please");
+
+echo "$delay\n";
+echo "foobar\n";
+ob_flush();
+flush();
+?>

Modified: trunk/Source/WebCore/ChangeLog (264852 => 264853)


--- trunk/Source/WebCore/ChangeLog	2020-07-24 19:59:56 UTC (rev 264852)
+++ trunk/Source/WebCore/ChangeLog	2020-07-24 20:48:42 UTC (rev 264853)
@@ -1,3 +1,59 @@
+2020-07-24  Jiewen Tan  <[email protected]>
+
+        Propagating user gesture through Fetch API
+        https://bugs.webkit.org/show_bug.cgi?id=214444
+        <rdar://problem/65980953>
+
+        Reviewed by Youenn Fablet.
+
+        This patch propagates media only user gestures through Fetch API, including Fetch and FetchResponse.
+
+        Covered by new test contents.
+
+        * Modules/fetch/DOMWindowFetch.cpp:
+        (WebCore::DOMWindowFetch::fetch):
+        Propagates media only user gestures through Fetch.
+
+        * Modules/fetch/FetchBodyConsumer.cpp:
+        (WebCore::FetchBodyConsumer::clean):
+        (WebCore::FetchBodyConsumer::resolve):
+        (WebCore::FetchBodyConsumer::setConsumePromise):
+        (WebCore::FetchBodyConsumer::resetConsumePromise):
+        (WebCore::FetchBodyConsumer::loadingFailed):
+        (WebCore::FetchBodyConsumer::loadingSucceeded):
+        * Modules/fetch/FetchBodyConsumer.h:
+        (WebCore::FetchBodyConsumer::setMaximumIntervalForUserGestureForwarding):
+        (WebCore::FetchBodyConsumer::FetchBodyConsumer):
+        Propagates media only user gestures through FetchResponse. UserGestureIndicator is only set in the async promise resolving path.
+
+        * WebCore.xcodeproj/project.pbxproj:
+        * bindings/js/JSDOMWindowBase.cpp:
+        (WebCore::JSDOMWindowBase::queueMicrotaskToEventLoop):
+        Propagates media only user gestures that was propagated through Fetch to the related promise executors.
+
+        * dom/UserGestureIndicator.cpp:
+        (WebCore::UserGestureIndicator::UserGestureIndicator):
+        (WebCore::UserGestureIndicator::~UserGestureIndicator):
+        * dom/UserGestureIndicator.h:
+        (WebCore::UserGestureToken::setIsPropagatedFromFetch):
+        (WebCore::UserGestureToken::resetIsPropagatedFromFetch):
+        (WebCore::UserGestureToken::isPropagatedFromFetch const):
+        Adds a flag to indicate the propagation source of a user gesture token. And makes pipelines from testing.
+
+        * page/DOMTimer.cpp:
+        (WebCore::DOMTimer::fired):
+        Gathers some constants.
+
+        * testing/Internals.cpp:
+        (WebCore::Internals::setFetchMaximumIntervalForUserGestureForwarding):
+        * testing/Internals.h:
+        * testing/Internals.idl:
+        Crafts a way to pass the maximum interval from layout tests.
+
+        * xml/XMLHttpRequest.cpp:
+        (WebCore::XMLHttpRequest::XMLHttpRequest):
+        Gathers some constants.
+
 2020-07-24  Kenneth Russell  <[email protected]>
 
         [WebGL2] Framebuffer and renderbuffer updates

Modified: trunk/Source/WebCore/Modules/fetch/DOMWindowFetch.cpp (264852 => 264853)


--- trunk/Source/WebCore/Modules/fetch/DOMWindowFetch.cpp	2020-07-24 19:59:56 UTC (rev 264852)
+++ trunk/Source/WebCore/Modules/fetch/DOMWindowFetch.cpp	2020-07-24 20:48:42 UTC (rev 264853)
@@ -35,6 +35,7 @@
 #include "FetchResponse.h"
 #include "JSDOMPromiseDeferred.h"
 #include "JSFetchResponse.h"
+#include "UserGestureIndicator.h"
 
 namespace WebCore {
 
@@ -56,7 +57,13 @@
         return;
     }
 
-    FetchResponse::fetch(*document, request.releaseReturnValue().get(), [promise = WTFMove(promise)](ExceptionOr<FetchResponse&>&& result) mutable {
+    FetchResponse::fetch(*document, request.releaseReturnValue(), [promise = WTFMove(promise), userGestureToken = UserGestureIndicator::currentUserGesture()](ExceptionOr<FetchResponse&>&& result) mutable {
+        if (!userGestureToken || userGestureToken->hasExpired(UserGestureToken::maximumIntervalForUserGestureForwardingForFetch()) || !userGestureToken->processingUserGesture()) {
+            promise.settle(WTFMove(result));
+            return;
+        }
+
+        UserGestureIndicator gestureIndicator(userGestureToken, UserGestureToken::GestureScope::MediaOnly, UserGestureToken::IsPropagatedFromFetch::Yes);
         promise.settle(WTFMove(result));
     });
 }

Modified: trunk/Source/WebCore/Modules/fetch/FetchBodyConsumer.cpp (264852 => 264853)


--- trunk/Source/WebCore/Modules/fetch/FetchBodyConsumer.cpp	2020-07-24 19:59:56 UTC (rev 264852)
+++ trunk/Source/WebCore/Modules/fetch/FetchBodyConsumer.cpp	2020-07-24 20:48:42 UTC (rev 264853)
@@ -68,7 +68,7 @@
 void FetchBodyConsumer::clean()
 {
     m_buffer = nullptr;
-    m_consumePromise = nullptr;
+    resetConsumePromise();
     if (m_sink) {
         m_sink->clearCallback();
         return;
@@ -107,7 +107,7 @@
     }
 
     if (m_isLoading) {
-        m_consumePromise = WTFMove(promise);
+        setConsumePromise(WTFMove(promise));
         return;
     }
 
@@ -189,9 +189,16 @@
 void FetchBodyConsumer::setConsumePromise(Ref<DeferredPromise>&& promise)
 {
     ASSERT(!m_consumePromise);
+    m_userGestureToken = UserGestureIndicator::currentUserGesture();
     m_consumePromise = WTFMove(promise);
 }
 
+void FetchBodyConsumer::resetConsumePromise()
+{
+    m_consumePromise = nullptr;
+    m_userGestureToken = nullptr;
+}
+
 void FetchBodyConsumer::setSource(Ref<FetchBodySource>&& source)
 {
     m_source = WTFMove(source);
@@ -206,7 +213,7 @@
     m_isLoading = false;
     if (m_consumePromise) {
         m_consumePromise->reject(exception);
-        m_consumePromise = nullptr;
+        resetConsumePromise();
     }
     if (m_source) {
         m_source->error(exception);
@@ -218,8 +225,14 @@
 {
     m_isLoading = false;
 
-    if (m_consumePromise)
-        resolve(m_consumePromise.releaseNonNull(), nullptr);
+    if (m_consumePromise) {
+        if (!m_userGestureToken || m_userGestureToken->hasExpired(UserGestureToken::maximumIntervalForUserGestureForwardingForFetch()) || !m_userGestureToken->processingUserGesture())
+            resolve(m_consumePromise.releaseNonNull(), nullptr);
+        else {
+            UserGestureIndicator gestureIndicator(m_userGestureToken, UserGestureToken::GestureScope::MediaOnly, UserGestureToken::IsPropagatedFromFetch::Yes);
+            resolve(m_consumePromise.releaseNonNull(), nullptr);
+        }
+    }
     if (m_source) {
         m_source->close();
         m_source = nullptr;

Modified: trunk/Source/WebCore/Modules/fetch/FetchBodyConsumer.h (264852 => 264853)


--- trunk/Source/WebCore/Modules/fetch/FetchBodyConsumer.h	2020-07-24 19:59:56 UTC (rev 264852)
+++ trunk/Source/WebCore/Modules/fetch/FetchBodyConsumer.h	2020-07-24 20:48:42 UTC (rev 264853)
@@ -32,6 +32,7 @@
 #include "JSDOMPromiseDeferred.h"
 #include "ReadableStreamSink.h"
 #include "SharedBuffer.h"
+#include "UserGestureIndicator.h"
 
 namespace WebCore {
 
@@ -43,7 +44,7 @@
 public:
     enum class Type { None, ArrayBuffer, Blob, JSON, Text };
 
-    FetchBodyConsumer(Type type) : m_type(type) { }
+    explicit FetchBodyConsumer(Type type) : m_type(type) { }
 
     void append(const char* data, unsigned);
     void append(const unsigned char* data, unsigned);
@@ -75,6 +76,7 @@
 
 private:
     Ref<Blob> takeAsBlob();
+    void resetConsumePromise();
 
     Type m_type;
     String m_contentType;
@@ -83,6 +85,7 @@
     RefPtr<ReadableStreamToSharedBufferSink> m_sink;
     RefPtr<FetchBodySource> m_source;
     bool m_isLoading { false };
+    RefPtr<UserGestureToken> m_userGestureToken;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/Modules/fetch/FetchRequest.cpp (264852 => 264853)


--- trunk/Source/WebCore/Modules/fetch/FetchRequest.cpp	2020-07-24 19:59:56 UTC (rev 264852)
+++ trunk/Source/WebCore/Modules/fetch/FetchRequest.cpp	2020-07-24 20:48:42 UTC (rev 264853)
@@ -334,4 +334,3 @@
 }
 
 } // namespace WebCore
-

Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (264852 => 264853)


--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2020-07-24 19:59:56 UTC (rev 264852)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2020-07-24 20:48:42 UTC (rev 264853)
@@ -1762,6 +1762,7 @@
 		5704405A1E53936200356601 /* JSAesCbcCfbParams.h in Headers */ = {isa = PBXBuildFile; fileRef = 570440591E53936200356601 /* JSAesCbcCfbParams.h */; };
 		5706A6961DDE5C9500A03B14 /* CryptoAlgorithmRsaOaepParams.h in Headers */ = {isa = PBXBuildFile; fileRef = 5706A6951DDE5C9500A03B14 /* CryptoAlgorithmRsaOaepParams.h */; };
 		5706A6981DDE5E4600A03B14 /* JSRsaOaepParams.h in Headers */ = {isa = PBXBuildFile; fileRef = 5706A6971DDE5E4600A03B14 /* JSRsaOaepParams.h */; };
+		57093D1324C97C8F0086C52D /* FetchResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 413015D61C7B570400091C6E /* FetchResponse.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		571252691E524EB1008FF369 /* CryptoAlgorithmAES_CFB.h in Headers */ = {isa = PBXBuildFile; fileRef = 571252681E524EB1008FF369 /* CryptoAlgorithmAES_CFB.h */; };
 		57152B5A21CB3E88000C37CA /* ApduCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 57152B5821CB2E3B000C37CA /* ApduCommand.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		57152B5C21CC1902000C37CA /* ApduResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 57152B5621CB2E3A000C37CA /* ApduResponse.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -1812,6 +1813,7 @@
 		5750A97E1E6A13EF00705C4A /* CryptoAlgorithmEcKeyParams.h in Headers */ = {isa = PBXBuildFile; fileRef = 5750A97D1E6A13EF00705C4A /* CryptoAlgorithmEcKeyParams.h */; };
 		5750A9821E6A150800705C4A /* JSEcKeyParams.h in Headers */ = {isa = PBXBuildFile; fileRef = 5750A9801E6A150800705C4A /* JSEcKeyParams.h */; };
 		5750A9871E6A216800705C4A /* CryptoAlgorithmECDH.h in Headers */ = {isa = PBXBuildFile; fileRef = 5750A9851E6A216800705C4A /* CryptoAlgorithmECDH.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		57533E0824C6C598003EF2D3 /* FetchRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 41F54F881C50C4F600338488 /* FetchRequest.h */; };
 		5754719F1ECE628300DD63B2 /* JSRsaPssParams.h in Headers */ = {isa = PBXBuildFile; fileRef = 575471991ECE5D2A00DD63B2 /* JSRsaPssParams.h */; };
 		57585961239F124D00C74572 /* JSAuthenticationExtensionsClientOutputs.h in Headers */ = {isa = PBXBuildFile; fileRef = 5758595E239F117300C74572 /* JSAuthenticationExtensionsClientOutputs.h */; };
 		5758596C239F321C00C74572 /* AuthenticationExtensionsClientOutputs.h in Headers */ = {isa = PBXBuildFile; fileRef = 57585965239F14CC00C74572 /* AuthenticationExtensionsClientOutputs.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -30808,11 +30810,13 @@
 				517A53581F5889E800DCDC0A /* FetchLoaderClient.h in Headers */,
 				41AD753A1CEF6BD100A31486 /* FetchOptions.h in Headers */,
 				7CE1914D1F2A9AFB00272F78 /* FetchReferrerPolicy.h in Headers */,
+				57533E0824C6C598003EF2D3 /* FetchRequest.h in Headers */,
 				7CE1915A1F2A9B3400272F78 /* FetchRequestCache.h in Headers */,
 				7CE191551F2A9B1D00272F78 /* FetchRequestCredentials.h in Headers */,
 				7CE1914A1F2A98B900272F78 /* FetchRequestInit.h in Headers */,
 				7CE191511F2A9B0A00272F78 /* FetchRequestMode.h in Headers */,
 				7CE191601F2A9B4F00272F78 /* FetchRequestRedirect.h in Headers */,
+				57093D1324C97C8F0086C52D /* FetchResponse.h in Headers */,
 				84730D8B1248F0B300D3A9C9 /* FETile.h in Headers */,
 				84730D8D1248F0B300D3A9C9 /* FETurbulence.h in Headers */,
 				FD31609512B026F700C1A359 /* FFTConvolver.h in Headers */,

Modified: trunk/Source/WebCore/bindings/js/JSDOMWindowBase.cpp (264852 => 264853)


--- trunk/Source/WebCore/bindings/js/JSDOMWindowBase.cpp	2020-07-24 19:59:56 UTC (rev 264852)
+++ trunk/Source/WebCore/bindings/js/JSDOMWindowBase.cpp	2020-07-24 20:48:42 UTC (rev 264853)
@@ -215,7 +215,17 @@
 
     auto callback = JSMicrotaskCallback::create(thisObject, WTFMove(task));
     auto& eventLoop = thisObject.scriptExecutionContext()->eventLoop();
-    eventLoop.queueMicrotask([callback = WTFMove(callback)]() mutable {
+    // Propagating media only user gesture for Fetch API's promise chain.
+    auto userGestureToken = UserGestureIndicator::currentUserGesture();
+    if (userGestureToken && !userGestureToken->isPropagatedFromFetch())
+        userGestureToken = nullptr;
+    eventLoop.queueMicrotask([callback = WTFMove(callback), userGestureToken = WTFMove(userGestureToken)]() mutable {
+        if (!userGestureToken) {
+            callback->call();
+            return;
+        }
+
+        UserGestureIndicator gestureIndicator(userGestureToken, UserGestureToken::GestureScope::MediaOnly, UserGestureToken::IsPropagatedFromFetch::Yes);
         callback->call();
     });
 }

Modified: trunk/Source/WebCore/dom/UserGestureIndicator.cpp (264852 => 264853)


--- trunk/Source/WebCore/dom/UserGestureIndicator.cpp	2020-07-24 19:59:56 UTC (rev 264852)
+++ trunk/Source/WebCore/dom/UserGestureIndicator.cpp	2020-07-24 20:48:42 UTC (rev 264853)
@@ -49,6 +49,17 @@
         observer(*this);
 }
 
+static Seconds maxIntervalForUserGestureForwardingForFetch { 10 };
+const Seconds& UserGestureToken::maximumIntervalForUserGestureForwardingForFetch()
+{
+    return maxIntervalForUserGestureForwardingForFetch;
+}
+
+void UserGestureToken::setMaximumIntervalForUserGestureForwardingForFetchForTesting(Seconds value)
+{
+    maxIntervalForUserGestureForwardingForFetch = WTFMove(value);
+}
+
 UserGestureIndicator::UserGestureIndicator(Optional<ProcessingUserGestureState> state, Document* document, UserGestureType gestureType, ProcessInteractionStyle processInteractionStyle)
     : m_previousToken { currentToken() }
 {
@@ -74,7 +85,7 @@
     }
 }
 
-UserGestureIndicator::UserGestureIndicator(RefPtr<UserGestureToken> token, UserGestureToken::GestureScope scope)
+UserGestureIndicator::UserGestureIndicator(RefPtr<UserGestureToken> token, UserGestureToken::GestureScope scope, UserGestureToken::IsPropagatedFromFetch isPropagatedFromFetch)
 {
     // Silently ignore UserGestureIndicators on non main threads.
     if (!isMainThread())
@@ -85,6 +96,7 @@
 
     if (token) {
         token->setScope(scope);
+        token->setIsPropagatedFromFetch(isPropagatedFromFetch);
         currentToken() = token;
     }
 }
@@ -97,6 +109,7 @@
     if (auto token = currentToken()) {
         token->resetDOMPasteAccess();
         token->resetScope();
+        token->resetIsPropagatedFromFetch();
     }
 
     currentToken() = m_previousToken;

Modified: trunk/Source/WebCore/dom/UserGestureIndicator.h (264852 => 264853)


--- trunk/Source/WebCore/dom/UserGestureIndicator.h	2020-07-24 19:59:56 UTC (rev 264852)
+++ trunk/Source/WebCore/dom/UserGestureIndicator.h	2020-07-24 20:48:42 UTC (rev 264853)
@@ -47,6 +47,10 @@
 
 class UserGestureToken : public RefCounted<UserGestureToken>, public CanMakeWeakPtr<UserGestureToken> {
 public:
+    static constexpr Seconds maximumIntervalForUserGestureForwarding { 1_s }; // One second matches Gecko.
+    static const Seconds& maximumIntervalForUserGestureForwardingForFetch();
+    WEBCORE_EXPORT static void setMaximumIntervalForUserGestureForwardingForFetchForTesting(Seconds);
+
     static Ref<UserGestureToken> create(ProcessingUserGestureState state, UserGestureType gestureType)
     {
         return adoptRef(*new UserGestureToken(state, gestureType));
@@ -84,6 +88,12 @@
     void setScope(GestureScope scope) { m_scope = scope; }
     void resetScope() { m_scope = GestureScope::All; }
 
+    // Expand the following methods if more propagation sources are added later.
+    enum class IsPropagatedFromFetch { Yes, No };
+    void setIsPropagatedFromFetch(IsPropagatedFromFetch is) { m_isPropagatedFromFetch = is; }
+    void resetIsPropagatedFromFetch() { m_isPropagatedFromFetch = IsPropagatedFromFetch::No; }
+    bool isPropagatedFromFetch() const { return m_isPropagatedFromFetch == IsPropagatedFromFetch::Yes; }
+
     bool hasExpired(Seconds expirationInterval) const
     {
         return m_startTime + expirationInterval < MonotonicTime::now();
@@ -104,6 +114,7 @@
     DOMPasteAccessPolicy m_domPasteAccessPolicy { DOMPasteAccessPolicy::NotRequestedYet };
     GestureScope m_scope { GestureScope::All };
     MonotonicTime m_startTime { MonotonicTime::now() };
+    IsPropagatedFromFetch m_isPropagatedFromFetch { IsPropagatedFromFetch::No };
 };
 
 class UserGestureIndicator {
@@ -118,7 +129,7 @@
     // If a document is provided, its last known user gesture timestamp is updated.
     enum class ProcessInteractionStyle { Immediate, Delayed };
     WEBCORE_EXPORT explicit UserGestureIndicator(Optional<ProcessingUserGestureState>, Document* = nullptr, UserGestureType = UserGestureType::Other, ProcessInteractionStyle = ProcessInteractionStyle::Immediate);
-    WEBCORE_EXPORT explicit UserGestureIndicator(RefPtr<UserGestureToken>, UserGestureToken::GestureScope = UserGestureToken::GestureScope::All);
+    WEBCORE_EXPORT explicit UserGestureIndicator(RefPtr<UserGestureToken>, UserGestureToken::GestureScope = UserGestureToken::GestureScope::All, UserGestureToken::IsPropagatedFromFetch = UserGestureToken::IsPropagatedFromFetch::No);
     WEBCORE_EXPORT ~UserGestureIndicator();
 
 private:

Modified: trunk/Source/WebCore/page/DOMTimer.cpp (264852 => 264853)


--- trunk/Source/WebCore/page/DOMTimer.cpp	2020-07-24 19:59:56 UTC (rev 264852)
+++ trunk/Source/WebCore/page/DOMTimer.cpp	2020-07-24 20:48:42 UTC (rev 264853)
@@ -48,7 +48,6 @@
 
 namespace WebCore {
 
-static const Seconds maxIntervalForUserGestureForwarding { 1_s }; // One second matches Gecko.
 static const Seconds minIntervalForNonUserObservableChangeTimers { 1_s }; // Empirically determined to maximize battery life.
 static const int maxTimerNestingLevel = 5;
 
@@ -301,7 +300,7 @@
 
     DOMTimerFireState fireState(context, std::min(m_nestingLevel + 1, maxTimerNestingLevel));
 
-    if (m_userGestureTokenToForward && m_userGestureTokenToForward->hasExpired(maxIntervalForUserGestureForwarding))
+    if (m_userGestureTokenToForward && m_userGestureTokenToForward->hasExpired(UserGestureToken::maximumIntervalForUserGestureForwarding))
         m_userGestureTokenToForward = nullptr;
 
     ASSERT(!isSuspended());

Modified: trunk/Source/WebCore/testing/Internals.cpp (264852 => 264853)


--- trunk/Source/WebCore/testing/Internals.cpp	2020-07-24 19:59:56 UTC (rev 264852)
+++ trunk/Source/WebCore/testing/Internals.cpp	2020-07-24 20:48:42 UTC (rev 264853)
@@ -5672,9 +5672,9 @@
     page->diagnosticLoggingClient().logDiagnosticMessageWithValueDictionary("testMessage"_s, "testDescription"_s, dictionary, ShouldSample::No);
 }
 
-void Internals::setXHRMaximumIntervalForUserGestureForwarding(XMLHttpRequest& request, double interval)
+void Internals::setMaximumIntervalForUserGestureForwardingForFetch(double interval)
 {
-    request.setMaximumIntervalForUserGestureForwarding(interval);
+    UserGestureToken::setMaximumIntervalForUserGestureForwardingForFetchForTesting(Seconds(interval));
 }
 
 void Internals::setTransientActivationDuration(double seconds)

Modified: trunk/Source/WebCore/testing/Internals.h (264852 => 264853)


--- trunk/Source/WebCore/testing/Internals.h	2020-07-24 19:59:56 UTC (rev 264852)
+++ trunk/Source/WebCore/testing/Internals.h	2020-07-24 20:48:42 UTC (rev 264853)
@@ -961,7 +961,7 @@
 
     void testDictionaryLogging();
 
-    void setXHRMaximumIntervalForUserGestureForwarding(XMLHttpRequest&, double);
+    void setMaximumIntervalForUserGestureForwardingForFetch(double);
     void setTransientActivationDuration(double seconds);
 
     void setIsPlayingToAutomotiveHeadUnit(bool);

Modified: trunk/Source/WebCore/testing/Internals.idl (264852 => 264853)


--- trunk/Source/WebCore/testing/Internals.idl	2020-07-24 19:59:56 UTC (rev 264852)
+++ trunk/Source/WebCore/testing/Internals.idl	2020-07-24 20:48:42 UTC (rev 264853)
@@ -907,7 +907,7 @@
 
     void testDictionaryLogging();
 
-    void setXHRMaximumIntervalForUserGestureForwarding(XMLHttpRequest xhr, double interval);
+    void setMaximumIntervalForUserGestureForwardingForFetch(double interval);
     void setTransientActivationDuration(double seconds);
 
     void setIsPlayingToAutomotiveHeadUnit(boolean value);

Modified: trunk/Source/WebCore/xml/XMLHttpRequest.cpp (264852 => 264853)


--- trunk/Source/WebCore/xml/XMLHttpRequest.cpp	2020-07-24 19:59:56 UTC (rev 264852)
+++ trunk/Source/WebCore/xml/XMLHttpRequest.cpp	2020-07-24 20:48:42 UTC (rev 264853)
@@ -67,8 +67,6 @@
 
 namespace WebCore {
 
-static const Seconds maximumIntervalForUserGestureForwarding { 10_s };
-
 WTF_MAKE_ISO_ALLOCATED_IMPL(XMLHttpRequest);
 
 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, xmlHttpRequestCounter, ("XMLHttpRequest"));
@@ -121,7 +119,6 @@
     , m_responseType(static_cast<unsigned>(ResponseType::EmptyString))
     , m_progressEventThrottle(*this)
     , m_timeoutTimer(*this, &XMLHttpRequest::didReachTimeout)
-    , m_maximumIntervalForUserGestureForwarding(maximumIntervalForUserGestureForwarding)
 {
 #ifndef NDEBUG
     xmlHttpRequestCounter.increment();
@@ -1081,7 +1078,7 @@
 {
     RELEASE_ASSERT(!scriptExecutionContext()->activeDOMObjectsAreSuspended());
 
-    if (m_userGestureToken && m_userGestureToken->hasExpired(m_maximumIntervalForUserGestureForwarding))
+    if (m_userGestureToken && m_userGestureToken->hasExpired(UserGestureToken::maximumIntervalForUserGestureForwardingForFetch()))
         m_userGestureToken = nullptr;
 
     if (readyState() != DONE || !m_userGestureToken || !m_userGestureToken->processingUserGesture()) {
@@ -1156,11 +1153,6 @@
     ActiveDOMObject::contextDestroyed();
 }
 
-void XMLHttpRequest::setMaximumIntervalForUserGestureForwarding(double interval)
-{
-    m_maximumIntervalForUserGestureForwarding = Seconds(interval);    
-}
-
 void XMLHttpRequest::eventListenersDidChange()
 {
     m_hasRelevantEventListener = hasEventListeners(eventNames().abortEvent)

Modified: trunk/Source/WebCore/xml/XMLHttpRequest.h (264852 => 264853)


--- trunk/Source/WebCore/xml/XMLHttpRequest.h	2020-07-24 19:59:56 UTC (rev 264852)
+++ trunk/Source/WebCore/xml/XMLHttpRequest.h	2020-07-24 20:48:42 UTC (rev 264853)
@@ -128,8 +128,6 @@
 
     size_t memoryCost() const;
 
-    WEBCORE_EXPORT void setMaximumIntervalForUserGestureForwarding(double);
-
     using EventTarget::dispatchEvent;
     void dispatchEvent(Event&) override;
 
@@ -247,7 +245,6 @@
 
     Optional<ExceptionCode> m_exceptionCode;
     RefPtr<UserGestureToken> m_userGestureToken;
-    Seconds m_maximumIntervalForUserGestureForwarding;
     bool m_hasRelevantEventListener { false };
 };
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to