WebKit Bugzilla
Attachment 368599 Details for
Bug 197428
: XMLHttpRequest should propagate user gestures for media playback
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-197428-20190430131235.patch (text/plain), 24.88 KB, created by
Eric Carlson
on 2019-04-30 13:12:36 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Eric Carlson
Created:
2019-04-30 13:12:36 PDT
Size:
24.88 KB
patch
obsolete
>Subversion Revision: 244732 >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index d3c94590602595c42527d99f8caad2d74a933749..e6aae2451a0f51c23e2b1e5b6b698c9c3a2d9c5c 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,50 @@ >+2019-04-30 Eric Carlson <eric.carlson@apple.com> >+ >+ XMLHttpRequest should propagate user gestures for media playback >+ https://bugs.webkit.org/show_bug.cgi?id=197428 >+ <rdar://problem/46677392> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ A user gesture the would allow media state change in effect when XMLHttpRequest.send is >+ called should be active when the event handlers fire after the transaction completes successfully. >+ >+ Test: http/tests/media/user-gesture-preserved-across-xmlhttprequest.html >+ >+ * dom/UserGestureIndicator.cpp: >+ (WebCore::UserGestureIndicator::UserGestureIndicator): Add a 'scope' parameter to potentially >+ limit the scope of the gesture to just media. >+ (WebCore::UserGestureIndicator::~UserGestureIndicator): Clear the scope. >+ * dom/UserGestureIndicator.h: >+ (WebCore::UserGestureToken::processingUserGesture const): >+ (WebCore::UserGestureToken::setScope): >+ (WebCore::UserGestureToken::resetScope): >+ (WebCore::UserGestureToken::hasExpired const): >+ >+ * page/DOMTimer.cpp: >+ (WebCore::DOMTimerFireState::DOMTimerFireState): Don't need to store the nested timer interval, >+ UserGestureIndicator knows when it started. >+ (WebCore::DOMTimer::DOMTimer): Ditto. >+ (WebCore::DOMTimer::fired): Ditto. >+ (WebCore::DOMTimerFireState::nestedTimerInterval const): Deleted. >+ (WebCore::shouldForwardUserGesture): Deleted. >+ (WebCore::userGestureTokenToForward): Deleted. >+ (WebCore::currentNestedTimerInterval): Deleted. >+ * page/DOMTimer.h: >+ >+ * testing/Internals.cpp: >+ (WebCore::Internals::setXHRMaximumIntervalForUserGestureForwarding): Override the maximum >+ user gesture interval for testing. >+ * testing/Internals.h: >+ * testing/Internals.idl: >+ >+ * xml/XMLHttpRequest.cpp: >+ (WebCore::XMLHttpRequest::XMLHttpRequest): >+ (WebCore::XMLHttpRequest::send): Stash the user gesture token. >+ (WebCore::XMLHttpRequest::dispatchEvent): Clear user gesture token if it has expired. If still >+ valid, activate it. >+ * xml/XMLHttpRequest.h: >+ > 2019-04-25 Carlos Garcia Campos <cgarcia@igalia.com> > > REGRESSION(r244635): [GTK] Wrong background color used in non-dark mode >diff --git a/Source/WebCore/dom/UserGestureIndicator.cpp b/Source/WebCore/dom/UserGestureIndicator.cpp >index 0460c816f060111f653be6fd287495bb11a1ab0c..0ec20b194cdd15abb5d473cc3709d16957b88b4b 100644 >--- a/Source/WebCore/dom/UserGestureIndicator.cpp >+++ b/Source/WebCore/dom/UserGestureIndicator.cpp >@@ -70,7 +70,7 @@ UserGestureIndicator::UserGestureIndicator(Optional<ProcessingUserGestureState> > } > } > >-UserGestureIndicator::UserGestureIndicator(RefPtr<UserGestureToken> token) >+UserGestureIndicator::UserGestureIndicator(RefPtr<UserGestureToken> token, UserGestureToken::GestureScope scope) > { > // Silently ignore UserGestureIndicators on non main threads. > if (!isMainThread()) >@@ -79,8 +79,10 @@ UserGestureIndicator::UserGestureIndicator(RefPtr<UserGestureToken> token) > // It is only safe to use currentToken() on the main thread. > m_previousToken = currentToken(); > >- if (token) >+ if (token) { >+ token->setScope(scope); > currentToken() = token; >+ } > } > > UserGestureIndicator::~UserGestureIndicator() >@@ -88,8 +90,10 @@ UserGestureIndicator::~UserGestureIndicator() > if (!isMainThread()) > return; > >- if (auto token = currentToken()) >+ if (auto token = currentToken()) { > token->resetDOMPasteAccess(); >+ token->resetScope(); >+ } > > currentToken() = m_previousToken; > } >diff --git a/Source/WebCore/dom/UserGestureIndicator.h b/Source/WebCore/dom/UserGestureIndicator.h >index 66bf7ddaa25d10a27c8c7f83d01d0a7cfa96f2c0..cca4240edce31c3ee5f7389c14a4aa738435ea99 100644 >--- a/Source/WebCore/dom/UserGestureIndicator.h >+++ b/Source/WebCore/dom/UserGestureIndicator.h >@@ -27,6 +27,7 @@ > > #include "DOMPasteAccess.h" > #include <wtf/Function.h> >+#include <wtf/MonotonicTime.h> > #include <wtf/Noncopyable.h> > #include <wtf/RefCounted.h> > #include <wtf/Vector.h> >@@ -53,7 +54,7 @@ public: > WEBCORE_EXPORT ~UserGestureToken(); > > ProcessingUserGestureState state() const { return m_state; } >- bool processingUserGesture() const { return m_state == ProcessingUserGesture; } >+ bool processingUserGesture() const { return m_scope == GestureScope::All && m_state == ProcessingUserGesture; } > bool processingUserGestureForMedia() const { return m_state == ProcessingUserGesture || m_state == ProcessingPotentialUserGesture; } > UserGestureType gestureType() const { return m_gestureType; } > >@@ -78,6 +79,15 @@ public: > } > void resetDOMPasteAccess() { m_domPasteAccessPolicy = DOMPasteAccessPolicy::NotRequestedYet; } > >+ enum class GestureScope { All, MediaOnly }; >+ void setScope(GestureScope scope) { m_scope = scope; } >+ void resetScope() { m_scope = GestureScope::All; } >+ >+ bool hasExpired(Seconds expirationInterval) const >+ { >+ return m_startTime + expirationInterval < MonotonicTime::now(); >+ } >+ > private: > UserGestureToken(ProcessingUserGestureState state, UserGestureType gestureType) > : m_state(state) >@@ -89,6 +99,8 @@ private: > Vector<WTF::Function<void (UserGestureToken&)>> m_destructionObservers; > UserGestureType m_gestureType; > DOMPasteAccessPolicy m_domPasteAccessPolicy { DOMPasteAccessPolicy::NotRequestedYet }; >+ GestureScope m_scope { GestureScope::All }; >+ MonotonicTime m_startTime { MonotonicTime::now() }; > }; > > class UserGestureIndicator { >@@ -103,7 +115,7 @@ public: > // 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>); >+ WEBCORE_EXPORT explicit UserGestureIndicator(RefPtr<UserGestureToken>, UserGestureToken::GestureScope = UserGestureToken::GestureScope::All); > WEBCORE_EXPORT ~UserGestureIndicator(); > > private: >diff --git a/Source/WebCore/page/DOMTimer.cpp b/Source/WebCore/page/DOMTimer.cpp >index 64f26547a9c3ff40390c502a73aafbbcf483b041..6a22dbe7c2c98e160d22ed5bc1c9eb6426fb5d0d 100644 >--- a/Source/WebCore/page/DOMTimer.cpp >+++ b/Source/WebCore/page/DOMTimer.cpp >@@ -53,9 +53,8 @@ static const int maxTimerNestingLevel = 5; > > class DOMTimerFireState { > public: >- DOMTimerFireState(ScriptExecutionContext& context, int nestingLevel, const Seconds& nestedTimerInterval) >+ DOMTimerFireState(ScriptExecutionContext& context, int nestingLevel) > : m_context(context) >- , m_nestedTimerInterval(nestedTimerInterval) > , m_contextIsDocument(is<Document>(m_context)) > { > // For worker threads, don't update the current DOMTimerFireState. >@@ -78,8 +77,6 @@ public: > > Document* contextDocument() const { return m_contextIsDocument ? &downcast<Document>(m_context) : nullptr; } > >- const Seconds& nestedTimerInterval() const { return m_nestedTimerInterval; } >- > void setScriptMadeUserObservableChanges() { m_scriptMadeUserObservableChanges = true; } > void setScriptMadeNonUserObservableChanges() { m_scriptMadeNonUserObservableChanges = true; } > >@@ -98,7 +95,6 @@ public: > > private: > ScriptExecutionContext& m_context; >- Seconds m_nestedTimerInterval; > uint64_t m_initialDOMTreeVersion; > DOMTimerFireState* m_previous; > bool m_contextIsDocument; >@@ -163,27 +159,6 @@ private: > > bool NestedTimersMap::isTrackingNestedTimers = false; > >-static inline bool shouldForwardUserGesture(Seconds interval) >-{ >- return UserGestureIndicator::processingUserGesture() >- && interval <= maxIntervalForUserGestureForwarding; >-} >- >-static inline RefPtr<UserGestureToken> userGestureTokenToForward(Seconds interval) >-{ >- if (!shouldForwardUserGesture(interval)) >- return nullptr; >- >- return UserGestureIndicator::currentUserGesture(); >-} >- >-static inline Seconds currentNestedTimerInterval() >-{ >- if (DOMTimerFireState::current) >- return DOMTimerFireState::current->nestedTimerInterval(); >- return { }; >-} >- > DOMTimer::DOMTimer(ScriptExecutionContext& context, std::unique_ptr<ScheduledAction> action, Seconds interval, bool singleShot) > : SuspendableTimer(context) > , m_nestingLevel(context.timerNestingLevel()) >@@ -191,8 +166,7 @@ DOMTimer::DOMTimer(ScriptExecutionContext& context, std::unique_ptr<ScheduledAct > , m_originalInterval(interval) > , m_throttleState(Undetermined) > , m_currentTimerInterval(intervalClampedToMinimum()) >- , m_nestedTimerInterval(currentNestedTimerInterval()) >- , m_userGestureTokenToForward(userGestureTokenToForward(m_nestedTimerInterval + m_currentTimerInterval)) >+ , m_userGestureTokenToForward(UserGestureIndicator::currentUserGesture()) > { > RefPtr<DOMTimer> reference = adoptRef(this); > >@@ -310,7 +284,10 @@ void DOMTimer::fired() > ASSERT(scriptExecutionContext()); > ScriptExecutionContext& context = *scriptExecutionContext(); > >- DOMTimerFireState fireState(context, std::min(m_nestingLevel + 1, maxTimerNestingLevel), m_nestedTimerInterval + m_currentTimerInterval); >+ DOMTimerFireState fireState(context, std::min(m_nestingLevel + 1, maxTimerNestingLevel)); >+ >+ if (m_userGestureTokenToForward && m_userGestureTokenToForward->hasExpired(maxIntervalForUserGestureForwarding)) >+ m_userGestureTokenToForward = nullptr; > > ASSERT(!isSuspended()); > ASSERT(!context.activeDOMObjectsAreSuspended()); >diff --git a/Source/WebCore/page/DOMTimer.h b/Source/WebCore/page/DOMTimer.h >index 943865763da0a42948caaedcfe4badafc4d877d5..b9755a795b656ec542348f803f6dca65d69a6d23 100644 >--- a/Source/WebCore/page/DOMTimer.h >+++ b/Source/WebCore/page/DOMTimer.h >@@ -92,7 +92,6 @@ private: > Seconds m_originalInterval; > TimerThrottleState m_throttleState; > Seconds m_currentTimerInterval; >- Seconds m_nestedTimerInterval; > RefPtr<UserGestureToken> m_userGestureTokenToForward; > }; > >diff --git a/Source/WebCore/testing/Internals.cpp b/Source/WebCore/testing/Internals.cpp >index 64e03d849cb3b8f5ed6551c50e587fe30d1ba755..c4c6454bff74dab69b896775e4ee03d994bb4bde 100644 >--- a/Source/WebCore/testing/Internals.cpp >+++ b/Source/WebCore/testing/Internals.cpp >@@ -5021,4 +5021,9 @@ void Internals::testDictionaryLogging() > page->diagnosticLoggingClient().logDiagnosticMessageWithValueDictionary("testMessage"_s, "testDescription"_s, dictionary, ShouldSample::No); > } > >+void Internals::setXHRMaximumIntervalForUserGestureForwarding(XMLHttpRequest& request, double interval) >+{ >+ request.setMaximumIntervalForUserGestureForwarding(interval); >+} >+ > } // namespace WebCore >diff --git a/Source/WebCore/testing/Internals.h b/Source/WebCore/testing/Internals.h >index ee64537d249756c56534ed8169e495cedb58efc2..434092401acf5c48d1cf466c6339877460214691 100644 >--- a/Source/WebCore/testing/Internals.h >+++ b/Source/WebCore/testing/Internals.h >@@ -815,7 +815,9 @@ public: > void processDidResume(); > > void testDictionaryLogging(); >- >+ >+ void setXHRMaximumIntervalForUserGestureForwarding(XMLHttpRequest&, double); >+ > private: > explicit Internals(Document&); > Document* contextDocument() const; >diff --git a/Source/WebCore/testing/Internals.idl b/Source/WebCore/testing/Internals.idl >index f2069efb5ff48c59cb411ff3dcb4fd70b99bde70..00000c627e8ae4671e2d4bb4244fd12324f040f9 100644 >--- a/Source/WebCore/testing/Internals.idl >+++ b/Source/WebCore/testing/Internals.idl >@@ -746,4 +746,6 @@ enum CompositingPolicy { > void processDidResume(); > > void testDictionaryLogging(); >+ >+ void setXHRMaximumIntervalForUserGestureForwarding(XMLHttpRequest xhr, double interval); > }; >diff --git a/Source/WebCore/xml/XMLHttpRequest.cpp b/Source/WebCore/xml/XMLHttpRequest.cpp >index a20e256248e7a156ffbc7a047bd5dac02543762a..2f717c3a1fb31272d08b51d1eee80d8335aa317a 100644 >--- a/Source/WebCore/xml/XMLHttpRequest.cpp >+++ b/Source/WebCore/xml/XMLHttpRequest.cpp >@@ -66,6 +66,8 @@ > > namespace WebCore { > >+static const Seconds maximumIntervalForUserGestureForwarding { 10_s }; >+ > WTF_MAKE_ISO_ALLOCATED_IMPL(XMLHttpRequest); > > DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, xmlHttpRequestCounter, ("XMLHttpRequest")); >@@ -121,6 +123,7 @@ XMLHttpRequest::XMLHttpRequest(ScriptExecutionContext& context) > , m_resumeTimer(*this, &XMLHttpRequest::resumeTimerFired) > , m_networkErrorTimer(*this, &XMLHttpRequest::networkErrorTimerFired) > , m_timeoutTimer(*this, &XMLHttpRequest::didReachTimeout) >+ , m_maximumIntervalForUserGestureForwarding(maximumIntervalForUserGestureForwarding) > { > #ifndef NDEBUG > xmlHttpRequestCounter.increment(); >@@ -436,6 +439,7 @@ Optional<ExceptionOr<void>> XMLHttpRequest::prepareToSend() > ExceptionOr<void> XMLHttpRequest::send(Optional<SendTypes>&& sendType) > { > InspectorInstrumentation::willSendXMLHttpRequest(scriptExecutionContext(), url()); >+ m_userGestureToken = UserGestureIndicator::currentUserGesture(); > > ExceptionOr<void> result; > if (!sendType) >@@ -1089,6 +1093,20 @@ void XMLHttpRequest::didReceiveData(const char* data, int len) > } > } > >+void XMLHttpRequest::dispatchEvent(Event& event) >+{ >+ if (m_userGestureToken && m_userGestureToken->hasExpired(m_maximumIntervalForUserGestureForwarding)) >+ m_userGestureToken = nullptr; >+ >+ if (readyState() != DONE || !m_userGestureToken || !m_userGestureToken->processingUserGesture()) { >+ EventTarget::dispatchEvent(event); >+ return; >+ } >+ >+ UserGestureIndicator gestureIndicator(m_userGestureToken, UserGestureToken::GestureScope::MediaOnly); >+ EventTarget::dispatchEvent(event); >+} >+ > void XMLHttpRequest::dispatchErrorEvents(const AtomicString& type) > { > if (!m_uploadComplete) { >@@ -1188,4 +1206,9 @@ void XMLHttpRequest::contextDestroyed() > ActiveDOMObject::contextDestroyed(); > } > >+void XMLHttpRequest::setMaximumIntervalForUserGestureForwarding(double interval) >+{ >+ m_maximumIntervalForUserGestureForwarding = Seconds(interval); >+} >+ > } // namespace WebCore >diff --git a/Source/WebCore/xml/XMLHttpRequest.h b/Source/WebCore/xml/XMLHttpRequest.h >index 9299f4ce764ba34454d390b7dd9b124ef93b46d3..2da53e5fd5af96b30152494f018e49bab0fe4c81 100644 >--- a/Source/WebCore/xml/XMLHttpRequest.h >+++ b/Source/WebCore/xml/XMLHttpRequest.h >@@ -26,6 +26,7 @@ > #include "FormData.h" > #include "ResourceResponse.h" > #include "ThreadableLoaderClient.h" >+#include "UserGestureIndicator.h" > #include <wtf/URL.h> > #include "XMLHttpRequestEventTarget.h" > #include "XMLHttpRequestProgressEventThrottle.h" >@@ -127,6 +128,8 @@ public: > > size_t memoryCost() const; > >+ WEBCORE_EXPORT void setMaximumIntervalForUserGestureForwarding(double); >+ > private: > explicit XMLHttpRequest(ScriptExecutionContext&); > >@@ -187,6 +190,9 @@ private: > > void dispatchErrorEvents(const AtomicString&); > >+ using EventTarget::dispatchEvent; >+ void dispatchEvent(Event&) override; >+ > void resumeTimerFired(); > Ref<TextResourceDecoder> createDecoder() const; > >@@ -243,6 +249,8 @@ private: > MonotonicTime m_sendingTime; > > Optional<ExceptionCode> m_exceptionCode; >+ RefPtr<UserGestureToken> m_userGestureToken; >+ Seconds m_maximumIntervalForUserGestureForwarding; > }; > > inline auto XMLHttpRequest::responseType() const -> ResponseType >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index 218f6d5781da168399c2ff4f488a0ee304b303ed..622aedfeba6b70df511bc2c0d3068b255f3be29d 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,14 @@ >+2019-04-30 Eric Carlson <eric.carlson@apple.com> >+ >+ XMLHttpRequest should propagate user gestures for media playback >+ https://bugs.webkit.org/show_bug.cgi?id=197428 >+ <rdar://problem/46677392> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * http/tests/media/user-gesture-preserved-across-xmlhttprequest-expected.txt: Added. >+ * http/tests/media/user-gesture-preserved-across-xmlhttprequest.html: Added. >+ > 2019-04-29 Javier Fernandez <jfernandez@igalia.com> > > Update the CSS Text WPT test suite >diff --git a/LayoutTests/http/tests/media/user-gesture-preserved-across-xmlhttprequest-expected.txt b/LayoutTests/http/tests/media/user-gesture-preserved-across-xmlhttprequest-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..31c854cfe8fdd2145e9ba5e56bb6dfac33c32da7 >--- /dev/null >+++ b/LayoutTests/http/tests/media/user-gesture-preserved-across-xmlhttprequest-expected.txt >@@ -0,0 +1,56 @@ >+Test that a user gesture is preserved across XHR and setTimeout. >+ >+ >+Run! >+ >+** gesture -> XHR -> timeout -> XHR -> window.open: should fail because XHR only propagates user gesture for media >+sending XHR, delay = 100 >+EVENT(load): readyState = 4 >+setting timeout, delay = 100 >+sending XHR, delay = 100 >+EVENT(load): readyState = 4 >+EXPECTED (window.open("about:blank") == 'null') OK >+ >+** gesture -> timeout -> XHR -> timeout -> window.open: should succeed >+setting timeout, delay = 100 >+sending XHR, delay = 100 >+EVENT(load): readyState = 4 >+setting timeout, delay = 100 >+EXPECTED (window.open("about:blank") != 'null') OK >+ >+** gesture -> timeout -> XHR -> timeout -> video playback: should succeed >+EVENT(canplaythrough) >+setting timeout, delay = 100 >+sending XHR, delay = 100 >+EVENT(load): readyState = 4 >+setting timeout, delay = 100 >+RUN(shouldResolve(mediaElement.play()).then(testComplete, testComplete)) >+Promise resolved OK >+ >+** gesture -> XHR -> timeout -> XHR -> video playback: should succeed >+EVENT(canplaythrough) >+sending XHR, delay = 100 >+EVENT(load): readyState = 4 >+setting timeout, delay = 100 >+sending XHR, delay = 100 >+EVENT(load): readyState = 4 >+RUN(shouldResolve(mediaElement.play()).then(testComplete, testComplete)) >+Promise resolved OK >+ >+** NO gesture -> XHR -> timeout -> video playback: should fail >+EVENT(canplaythrough) >+sending XHR, delay = 100 >+EVENT(load): readyState = 4 >+setting timeout, delay = 100 >+RUN(shouldReject(mediaElement.play()).then(testComplete, testComplete)) >+Promise rejected correctly OK >+ >+** gesture -> "long" XHR -> video playback: should fail >+EVENT(canplaythrough) >+sending XHR, delay = 300 >+EVENT(load): readyState = 4 >+RUN(shouldReject(mediaElement.play()).then(testComplete, testComplete)) >+Promise rejected correctly OK >+ >+END OF TEST >+ >diff --git a/LayoutTests/http/tests/media/user-gesture-preserved-across-xmlhttprequest.html b/LayoutTests/http/tests/media/user-gesture-preserved-across-xmlhttprequest.html >new file mode 100644 >index 0000000000000000000000000000000000000000..82a2e9ba9a26d38372dbe38b97c01b23a866f75b >--- /dev/null >+++ b/LayoutTests/http/tests/media/user-gesture-preserved-across-xmlhttprequest.html >@@ -0,0 +1,166 @@ >+<!DOCTYPE html> >+<html> >+<head> >+ <meta charset='utf-8'> >+ <title>user-gesture-preserved-across-xmlhttprequest</title> >+ <script src='/media-resources/media-file.js'></script> >+ <script src='/media-resources/video-test.js'></script> >+ >+ <script> >+ let autoRun = true; >+ >+ if (window.testRunner) { >+ testRunner.dumpAsText(); >+ testRunner.waitUntilDone(); >+ testRunner.setCanOpenWindows(true); >+ } >+ >+ function doXHR(delay, timeout, completionHandler) >+ { >+ var xhr = new XMLHttpRequest(); >+ if (window.internals) >+ window.internals.setXHRMaximumIntervalForUserGestureForwarding(xhr, timeout); >+ >+ xhr.open('GET', `/xmlhttprequest/resources/download-with-delay.php?iteration=1&delay=${delay}`, true); >+ >+ xhr.onload = (evt) => { >+ consoleWrite(`EVENT(load): readyState = ${xhr.readyState}`); >+ if (xhr.readyState === 4) { >+ if (xhr.status === 200) { >+ completionHandler(); >+ } else { >+ logResult(Failed, `xhr.onload, status = ${xhr.statusText}`); >+ } >+ } >+ }; >+ >+ xhr.onerror = (err) => { >+ logResult(Failed, `xhr.onerror, status = ${xhr.statusText}`); >+ }; >+ >+ xhr.send(null); >+ } >+ >+ function testComplete() >+ { >+ if (autoRun) >+ nextTest(); >+ } >+ >+ function runTest(test) >+ { >+ if (!test.sequence.length) { >+ switch (test.action) { >+ case 'play': >+ if (test.success) >+ run("shouldResolve(mediaElement.play()).then(testComplete, testComplete)"); >+ else >+ run("shouldReject(mediaElement.play()).then(testComplete, testComplete)"); >+ break; >+ >+ case 'popup': >+ if (window.internals) >+ internals.settings.setJavaScriptCanOpenWindowsAutomatically(false); >+ testExpected('window.open("about:blank")', null, test.success ? '!=' : '=='); >+ testComplete(); >+ break; >+ } >+ return; >+ } >+ >+ let command = test.sequence.shift().trim().split(' '); >+ let delay = command[0]; >+ let method = command[1].toLowerCase(); >+ >+ switch (method) { >+ case 'xhr': >+ consoleWrite(`sending XHR, delay = ${delay}`); >+ doXHR(delay, test.timeout || 0.6, () => runTest(test)); >+ break; >+ case 'timeout': >+ consoleWrite(`setting timeout, delay = ${delay}`); >+ setTimeout(() => { runTest(test); }, delay); >+ break; >+ } >+ >+ } >+ >+ let tests = [ >+ { title : 'gesture -> XHR -> timeout -> XHR -> window.open: should fail because XHR only propagates user gesture for media', >+ action : 'popup', withkey : true, success : false, sequence : ['100 XHR', '100 timeout', '100 xhr'] }, >+ >+ { title : 'gesture -> timeout -> XHR -> timeout -> window.open: should succeed', >+ action : 'popup', withkey : true, success : true, sequence : ['100 timeout', '100 xhr', '100 timeout'] }, >+ >+ { title : 'gesture -> timeout -> XHR -> timeout -> video playback: should succeed', >+ action : 'play', withkey : true, success : true, sequence : ['100 timeout', '100 XHR', '100 timeout'] }, >+ >+ { title : 'gesture -> XHR -> timeout -> XHR -> video playback: should succeed', >+ action : 'play', withkey : true, success : true, sequence : ['100 XHR', '100 timeout', '100 xhr'] }, >+ >+ { title : 'NO gesture -> XHR -> timeout -> video playback: should fail', >+ 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'] }, >+ ]; >+ >+ function nextTest() >+ { >+ if (!tests.length) { >+ consoleWrite(''); >+ endTest() >+ return; >+ } >+ >+ let test = tests.shift(); >+ let action = () => { runTest(test) }; >+ consoleWrite(`<br>** ${test.title}`); >+ switch (test.action) { >+ case 'play': >+ let container = document.getElementById('parent'); >+ if (container.firstChild) >+ container.removeChild(container.firstChild); >+ >+ mediaElement = document.createElement('video'); >+ if (window.internals) >+ internals.setMediaElementRestrictions(mediaElement, 'RequireUserGestureForAudioRateChange'); >+ >+ mediaElement.src = "/media-resources/content/" + findMediaFile("video", "test"); >+ mediaElement.controls = 1; >+ container.appendChild(mediaElement); >+ waitForEvent('canplaythrough', event => { >+ if (test.withkey) >+ runWithKeyDown(action); >+ else >+ action(); >+ }); >+ >+ break; >+ >+ case 'popup': >+ if (test.withkey) >+ runWithKeyDown(action); >+ else >+ action(); >+ break; >+ } >+ } >+ >+ function start() >+ { >+ if (autoRun) >+ nextTest(); >+ } >+ >+ </script> >+</head> >+ >+<body onload="start()"> >+ <p>Test that a user gesture is preserved across XHR and setTimeout.</p> >+ <div id='parent'></div> >+ <button onclick="nextTest()">Run!</button><br> >+ <pre id='consoleLog'></pre> >+</body> >+</html> >+
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 197428
:
368599
|
368612
|
368613
|
368614