Diff
Modified: trunk/LayoutTests/ChangeLog (217062 => 217063)
--- trunk/LayoutTests/ChangeLog 2017-05-18 19:55:01 UTC (rev 217062)
+++ trunk/LayoutTests/ChangeLog 2017-05-18 20:32:01 UTC (rev 217063)
@@ -1,3 +1,17 @@
+2017-05-18 Jer Noble <[email protected]>
+
+ Allow nested timers to propagate user gestures so long as the total nested interval is less than 1s.
+ https://bugs.webkit.org/show_bug.cgi?id=172173
+
+ Reviewed by Andy Estes.
+
+ * fast/events/popup-blocked-from-untrusted-mouse-click.html:
+ * fast/events/popup-blocking-timers4-expected.txt: Removed.
+ * fast/events/popup-blocking-timers4.html: Removed.
+ * media/restricted-audio-playback-with-multiple-settimeouts-expected.txt: Added.
+ * media/restricted-audio-playback-with-multiple-settimeouts.html: Added.
+ * platform/ios/TestExpectations:
+
2017-05-18 Daniel Bates <[email protected]>
Evaluating window named element may return wrong result
Modified: trunk/LayoutTests/fast/events/popup-blocked-from-untrusted-mouse-click.html (217062 => 217063)
--- trunk/LayoutTests/fast/events/popup-blocked-from-untrusted-mouse-click.html 2017-05-18 19:55:01 UTC (rev 217062)
+++ trunk/LayoutTests/fast/events/popup-blocked-from-untrusted-mouse-click.html 2017-05-18 20:32:01 UTC (rev 217063)
@@ -38,7 +38,7 @@
evt.initMouseEvent("click", true, true, window,
0, 0, 0, 0, 0, false, false, false, false, 0, null);
var cb = document.getElementById("anchor");
- setTimeout(dispatchEvent(cb, evt), 100);
+ Promise.resolve().then(dispatchEvent(cb, evt));
}
function openWindow(evt) {
@@ -56,7 +56,7 @@
return;
}
// Go to next test stage.
- window.setTimeout(nextTestStage, 0);
+ Promise.resolve().then(nextTestStage);
}
function doClick() {
@@ -76,7 +76,7 @@
}
</script>
-<body _onload_="window.setTimeout(test, 0);">
+<body _onload_="test();">
<input type="button" _onclick_="simulateClick();" value="click me" id="btn"><br>
<a _onclick_="openWindow(event)" id="anchor"> open a new window </a><br>
The _javascript_ created (untrusted) event inside a user-initiated (trusted) event should not cache the UserGesture status. This test is for bug https://bugs.webkit.org/show_bug.cgi?id=50508.
Deleted: trunk/LayoutTests/fast/events/popup-blocking-timers4-expected.txt (217062 => 217063)
--- trunk/LayoutTests/fast/events/popup-blocking-timers4-expected.txt 2017-05-18 19:55:01 UTC (rev 217062)
+++ trunk/LayoutTests/fast/events/popup-blocking-timers4-expected.txt 2017-05-18 20:32:01 UTC (rev 217063)
@@ -1,4 +0,0 @@
-Click Here
-Test calling window.open() in a nested call to setTimeout(). A popup should not be allowed.
-PASS newWindow is null
-
Deleted: trunk/LayoutTests/fast/events/popup-blocking-timers4.html (217062 => 217063)
--- trunk/LayoutTests/fast/events/popup-blocking-timers4.html 2017-05-18 19:55:01 UTC (rev 217062)
+++ trunk/LayoutTests/fast/events/popup-blocking-timers4.html 2017-05-18 20:32:01 UTC (rev 217063)
@@ -1,42 +0,0 @@
-<!DOCTYPE html>
-<head>
- <script src=""
- <script>
- var newWindow;
-
- if (window.testRunner) {
- testRunner.dumpAsText();
- testRunner.setCanOpenWindows();
- testRunner.waitUntilDone();
- testRunner.setPopupBlockingEnabled(true);
- }
-
- function clickHandler() {
- setTimeout(function() {
- setTimeout(function() {
- newWindow = window.open("about:blank");
- self.focus();
- debug("Test calling window.open() in a nested call to setTimeout(). A popup should not be allowed.")
- shouldBeNull("newWindow");
- if (window.testRunner)
- testRunner.notifyDone();
- }, 0);
- }, 300);
- }
-
- function clickButton() {
- var button = document.getElementById("test");
- var buttonX = button.offsetLeft + button.offsetWidth / 2;
- var buttonY = button.offsetTop + button.offsetHeight / 2;
- if (window.eventSender) {
- eventSender.mouseMoveTo(buttonX, buttonY);
- eventSender.mouseDown();
- eventSender.mouseUp();
- }
- }
- </script>
-</head>
-<body _onload_="clickButton()">
- <button id="test" _onclick_="clickHandler()">Click Here</button>
- <div id="console"></div>
-</body>
Added: trunk/LayoutTests/media/restricted-audio-playback-with-multiple-settimeouts-expected.txt (0 => 217063)
--- trunk/LayoutTests/media/restricted-audio-playback-with-multiple-settimeouts-expected.txt (rev 0)
+++ trunk/LayoutTests/media/restricted-audio-playback-with-multiple-settimeouts-expected.txt 2017-05-18 20:32:01 UTC (rev 217063)
@@ -0,0 +1,30 @@
+Test that, when RequireUserGestureForAudioRateChange is set along with MediaUserGestureInheritsForDocument, nested setTimeout() calls of less than 1s will not block play() from succeeding.
+
+
+RUN(mediaElement.src = "" 'content/test'))
+EVENT(canplaythrough)
+Test > 1s in single step
+setTimeout 1, 1.1s
+RUN(shouldReject(mediaElement.play()).then(next, next))
+Promise rejected correctly OK
+
+RUN(mediaElement.src = "" 'content/test'))
+EVENT(canplaythrough)
+Test > 1s in multiple steps
+setTimeout 1, 0.6s
+setTimeout 1, 0.6s
+RUN(shouldReject(mediaElement.play()).then(next, next))
+Promise rejected correctly OK
+
+RUN(mediaElement.src = "" 'content/test'))
+EVENT(canplaythrough)
+Test < 1s total.
+setTimeout 1, 0.1s
+setTimeout 2, 0.1s
+setTimeout 3, 0.1s
+setTimeout 4, 0.1s
+setTimeout 5, 0.1s
+RUN(shouldResolve(mediaElement.play()).then(next, next))
+Promise resolved OK
+END OF TEST
+
Added: trunk/LayoutTests/media/restricted-audio-playback-with-multiple-settimeouts.html (0 => 217063)
--- trunk/LayoutTests/media/restricted-audio-playback-with-multiple-settimeouts.html (rev 0)
+++ trunk/LayoutTests/media/restricted-audio-playback-with-multiple-settimeouts.html 2017-05-18 20:32:01 UTC (rev 217063)
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>restricted-audio-playback-with-document-gesture</title>
+ <script src=""
+ <script src=""
+
+ <script>
+ function runTest()
+ {
+ next();
+ }
+
+ function next() {
+ if (!tests.length) {
+ endTest()
+ return;
+ }
+
+ mediaElement = document.createElement('audio');
+ if (window.internals)
+ internals.setMediaElementRestrictions(mediaElement, "RequireUserGestureForAudioRateChange");
+
+ var nextTest = tests.shift();
+ consoleWrite('');
+ run("mediaElement.src = "" 'content/test')");
+ waitForEvent('canplaythrough', event => { runWithKeyDown(nextTest) });
+ }
+
+ var tests = [
+ function() {
+ consoleWrite('Test > 1s in single step');
+ consoleWrite('setTimeout 1, 1.1s');
+ setTimeout(() => {
+ run("shouldReject(mediaElement.play()).then(next, next)");
+ }, 1100);
+ },
+
+
+ function() {
+ consoleWrite('Test > 1s in multiple steps');
+ consoleWrite('setTimeout 1, 0.6s');
+ setTimeout(() => {
+ consoleWrite('setTimeout 1, 0.6s');
+ setTimeout(() => {
+ run("shouldReject(mediaElement.play()).then(next, next)");
+ }, 600);
+ }, 600);
+ },
+
+ function() {
+ consoleWrite('Test < 1s total.')
+ consoleWrite('setTimeout 1, 0.1s');
+ setTimeout(() => {
+ consoleWrite('setTimeout 2, 0.1s');
+ setTimeout(() => {
+ consoleWrite('setTimeout 3, 0.1s');
+ setTimeout(() => {
+ consoleWrite('setTimeout 4, 0.1s');
+ setTimeout(() => {
+ consoleWrite('setTimeout 5, 0.1s');
+ setTimeout(() => {
+ run("shouldResolve(mediaElement.play()).then(next, next)");
+ }, 100);
+ }, 100);
+ }, 100);
+ }, 100);
+ }, 100);
+ },
+ ];
+ </script>
+</head>
+
+<body _onload_="runTest()">
+ <p>Test that, when RequireUserGestureForAudioRateChange is set along with MediaUserGestureInheritsForDocument,
+ nested setTimeout() calls of less than 1s will not block play() from succeeding.</p>
+</body>
+</html>
+
Modified: trunk/LayoutTests/platform/ios/TestExpectations (217062 => 217063)
--- trunk/LayoutTests/platform/ios/TestExpectations 2017-05-18 19:55:01 UTC (rev 217062)
+++ trunk/LayoutTests/platform/ios/TestExpectations 2017-05-18 20:32:01 UTC (rev 217063)
@@ -382,6 +382,7 @@
http/tests/navigation/keyboard-events-during-provisional-subframe-navigation.html [ Skip ]
media/audio-dealloc-crash.html [ Skip ]
media/restricted-audio-playback-with-document-gesture.html [ Skip ]
+media/restricted-audio-playback-with-multiple-settimeouts.html [ Skip ]
# Tests that use EventSender's mouseMoveTo, mouseUp and mouseDown
fast/forms/range/disabled-while-dragging.html [ Skip ]
Modified: trunk/Source/WebCore/ChangeLog (217062 => 217063)
--- trunk/Source/WebCore/ChangeLog 2017-05-18 19:55:01 UTC (rev 217062)
+++ trunk/Source/WebCore/ChangeLog 2017-05-18 20:32:01 UTC (rev 217063)
@@ -1,3 +1,31 @@
+2017-05-18 Jer Noble <[email protected]>
+
+ Allow nested timers to propagate user gestures so long as the total nested interval is less than 1s.
+ https://bugs.webkit.org/show_bug.cgi?id=172173
+
+ Reviewed by Andy Estes.
+
+ Test: media/restricted-audio-playback-with-multiple-settimeouts.html
+
+ Store the current nested timer interval in DOMTimerFireState, and use that value to propagate the
+ nested interval through multiple invocations of setTimeout().
+
+ Drive-by fix: instead of manually resetting the nesting level in DOMTimer::fired(), add the
+ nesting level to the DOMTimerFireState, and reset the nesting level on the state's destruction.
+ This fixes one place in DOMTimer::fire() where an early return lead to the timer's nesting level
+ not being reset.
+
+ * page/DOMTimer.cpp:
+ (WebCore::DOMTimerFireState::DOMTimerFireState):
+ (WebCore::DOMTimerFireState::~DOMTimerFireState):
+ (WebCore::DOMTimerFireState::nestedTimerInterval):
+ (WebCore::shouldForwardUserGesture):
+ (WebCore::userGestureTokenToForward):
+ (WebCore::currentNestedTimerInterval):
+ (WebCore::DOMTimer::DOMTimer):
+ (WebCore::DOMTimer::fired):
+ * page/DOMTimer.h:
+
2017-05-18 Youenn Fablet <[email protected]>
RealtimeOutgoingAudioSource should use the source sample rate
Modified: trunk/Source/WebCore/page/DOMTimer.cpp (217062 => 217063)
--- trunk/Source/WebCore/page/DOMTimer.cpp 2017-05-18 19:55:01 UTC (rev 217062)
+++ trunk/Source/WebCore/page/DOMTimer.cpp 2017-05-18 20:32:01 UTC (rev 217063)
@@ -58,8 +58,9 @@
class DOMTimerFireState {
public:
- explicit DOMTimerFireState(ScriptExecutionContext& context)
+ DOMTimerFireState(ScriptExecutionContext& context, int nestingLevel, const Seconds& nestedTimerInterval)
: m_context(context)
+ , m_nestedTimerInterval(nestedTimerInterval)
, m_contextIsDocument(is<Document>(m_context))
{
// For worker threads, don't update the current DOMTimerFireState.
@@ -69,6 +70,8 @@
m_previous = current;
current = this;
}
+
+ m_context.setTimerNestingLevel(nestingLevel);
}
~DOMTimerFireState()
@@ -75,10 +78,13 @@
{
if (m_contextIsDocument)
current = m_previous;
+ m_context.setTimerNestingLevel(0);
}
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; }
@@ -97,6 +103,7 @@
private:
ScriptExecutionContext& m_context;
+ Seconds m_nestedTimerInterval;
uint64_t m_initialDOMTreeVersion;
DOMTimerFireState* m_previous;
bool m_contextIsDocument;
@@ -161,21 +168,27 @@
bool NestedTimersMap::isTrackingNestedTimers = false;
-static inline bool shouldForwardUserGesture(Seconds interval, int nestingLevel)
+static inline bool shouldForwardUserGesture(Seconds interval)
{
return UserGestureIndicator::processingUserGesture()
- && interval <= maxIntervalForUserGestureForwarding
- && !nestingLevel; // Gestures should not be forwarded to nested timers.
+ && interval <= maxIntervalForUserGestureForwarding;
}
-static inline RefPtr<UserGestureToken> userGestureTokenToForward(Seconds interval, int nestingLevel)
+static inline RefPtr<UserGestureToken> userGestureTokenToForward(Seconds interval)
{
- if (!shouldForwardUserGesture(interval, nestingLevel))
+ 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())
@@ -183,7 +196,8 @@
, m_originalInterval(interval)
, m_throttleState(Undetermined)
, m_currentTimerInterval(intervalClampedToMinimum())
- , m_userGestureTokenToForward(userGestureTokenToForward(interval, m_nestingLevel))
+ , m_nestedTimerInterval(currentNestedTimerInterval())
+ , m_userGestureTokenToForward(userGestureTokenToForward(m_nestedTimerInterval + m_currentTimerInterval))
{
RefPtr<DOMTimer> reference = adoptRef(this);
@@ -302,10 +316,8 @@
ASSERT(scriptExecutionContext());
ScriptExecutionContext& context = *scriptExecutionContext();
- DOMTimerFireState fireState(context);
+ DOMTimerFireState fireState(context, std::min(m_nestingLevel + 1, maxTimerNestingLevel), m_nestedTimerInterval + m_currentTimerInterval);
- context.setTimerNestingLevel(std::min(m_nestingLevel + 1, maxTimerNestingLevel));
-
ASSERT(!isSuspended());
ASSERT(!context.activeDOMObjectsAreSuspended());
UserGestureIndicator gestureIndicator(m_userGestureTokenToForward);
@@ -378,8 +390,6 @@
}
nestedTimers->stopTracking();
}
-
- context.setTimerNestingLevel(0);
}
void DOMTimer::didStop()
Modified: trunk/Source/WebCore/page/DOMTimer.h (217062 => 217063)
--- trunk/Source/WebCore/page/DOMTimer.h 2017-05-18 19:55:01 UTC (rev 217062)
+++ trunk/Source/WebCore/page/DOMTimer.h 2017-05-18 20:32:01 UTC (rev 217063)
@@ -92,6 +92,7 @@
Seconds m_originalInterval;
TimerThrottleState m_throttleState;
Seconds m_currentTimerInterval;
+ Seconds m_nestedTimerInterval;
RefPtr<UserGestureToken> m_userGestureTokenToForward;
};