import changes from `dev' branch of rmottola/Arctic-Fox:

- Bug 1190469 - Refactor some code to have a general-purpose snap function available in APZC. r=botond (a4e8706df5)
- Bug 1225761 - Clear axis lock in CancelAnimation and EndTouch. r=botond (1436279d0d)
- Bug 1226316. Fixup to only expand displayport when skating with apz. r=kats (51c49a8fca)
- Bug 1181703 - Allow re-entering the panning state if a second finger is tapped while panning with one finger. r=botond (e87cd85058)
- Bug 1181703 - Add a gtest for making sure a pan can be continued after a second finger does a tap. r=botond (16354bec40)
- Bug 1190469 - Request scroll snapping in a few places that animations are cancelled so that we don't leave things unsnapped. r=botond (4b0ba5e513)
- Bug 960317 - Remove cross-slide code from APZC. r=botond (b2a045dbc9)
- Bug 1231972 - Document how the apz.fling_friction pref is used. r=kats. DONTBUILD for comment change (c181588265)
- Bug 1221593 - Don't reset other axis' scroll position during APZ drag. r=kats (9de4e134c6)
- Bug 1219929 - Fix bad MouseInputData case. r=me (3630a3fcfa)
- Bug 1211506 - Respect the APZ lock ordering in AsyncPanZoomController::OnScrollWheel(). r=mstange (3f152ffabf)
- Bug 1229125 - Reset velocity in OnPanEnd if there's nowhere to scroll. r=botond (66ec61692a)
- fix misspatch (f7aea52b9d)
- Bug 1221186 - Don't clobber a fling-snap with a snap-to-where-we-are-now in the overscroll handoff chain. r=botond (7d4862bbe6)
- Bug 1204932 - When C++APZ is enabled, elements that are zoomed with double tap should be centered. r=botond (b9e02a24b4)
- Bug 1141884 - Rename ResetInputState and make it only apply to touch events. r=botond (aab239f51a)
- Bug 1229125 - Correct velocity computation for pan gesture events. r=botond (b1f11b2e04)
- Bug 1228559 - get MSG by audio channel type. r=roc (0e5e32f9bb)
- Bug 1191207 - cancel chrome checking (74bde1263e)
- Bug 1155469 - Mark nsTextEditorState::mTextCtrlElement as MOZ_NON_OWNING_REF; r=baku (7e6f4fa9d0)
- Bug 1218072 - crash in nsTextEditorState::FinishedRestoringSelection, r=smaug (c7811e18ea)
- bits of 1190258 (7f8fbbddfa)
- Bug 1130237: [MSE] P1. Only ever return a frame if we have data. r=gerald (1ed595a5f8)
- Bug 1130237: P2. Reset decoder state even if no decoder has been created yet. r=gerald (d352c0314b)
- Bug 1130237: [MSE] P3. Add mochitest testing behavior. r=gerald (ce2b710a7e)
- Bug 1229987: [MSE] P1. Ensure next random access point properly calculated after seek. r=gerald (d9a435f0b5)
This commit is contained in:
2023-06-28 15:52:17 +08:00
parent 09bea9b394
commit 7d4a5ffc8c
19 changed files with 340 additions and 103 deletions
+2 -2
View File
@@ -4824,7 +4824,7 @@ HTMLMediaElement::NotifyAudioChannelAgent(bool aPlaying)
NS_IMETHODIMP HTMLMediaElement::WindowVolumeChanged(float aVolume, bool aMuted)
{
NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
MOZ_ASSERT(NS_IsMainThread());
UpdateChannelMuteState(aVolume, aMuted);
@@ -4993,7 +4993,7 @@ NS_IMETHODIMP HTMLMediaElement::WindowAudioCaptureChanged()
uint64_t id = window->WindowID();
MediaStreamGraph* msg =
MediaStreamGraph::GetInstance(MediaStreamGraph::AUDIO_THREAD_DRIVER,
AudioChannel::Normal);
mAudioChannel);
if (GetSrcMediaStream()) {
mCaptureStreamPort = msg->ConnectToCaptureStream(id, GetSrcMediaStream());
+4 -1
View File
@@ -110,7 +110,10 @@ public:
}
mTextEditorState->mSelectionRestoreEagerInit = false;
}
mTextEditorState->FinishedRestoringSelection();
if (mTextEditorState) {
mTextEditorState->FinishedRestoringSelection();
}
return NS_OK;
}
+4 -1
View File
@@ -13,6 +13,7 @@
#include "nsITextControlFrame.h"
#include "nsCycleCollectionParticipant.h"
#include "mozilla/dom/Element.h"
#include "mozilla/Attributes.h"
#include "mozilla/WeakPtr.h"
class nsTextInputListener;
@@ -282,7 +283,9 @@ private:
friend class InitializationGuard;
friend class PrepareEditorEvent;
nsITextControlElement* const mTextCtrlElement;
// The text control element owns this object, and ensures that this object
// has a smaller lifetime.
nsITextControlElement* const MOZ_NON_OWNING_REF mTextCtrlElement;
RefPtr<nsTextInputSelectionImpl> mSelCon;
RefPtr<RestoreSelectionState> mRestoringSelection;
nsCOMPtr<nsIEditor> mEditor;
+8 -3
View File
@@ -476,9 +476,9 @@ VideoData::Create(const VideoInfo& aInfo,
MediaRawData::MediaRawData()
: MediaData(RAW_DATA, 0)
, mCrypto(mCryptoInternal)
, mData(nullptr)
, mSize(0)
, mCrypto(mCryptoInternal)
, mBuffer(nullptr)
, mCapacity(0)
{
@@ -486,15 +486,17 @@ MediaRawData::MediaRawData()
MediaRawData::MediaRawData(const uint8_t* aData, size_t aSize)
: MediaData(RAW_DATA, 0)
, mCrypto(mCryptoInternal)
, mData(nullptr)
, mSize(0)
, mCrypto(mCryptoInternal)
, mBuffer(nullptr)
, mCapacity(0)
{
if (!EnsureCapacity(aSize)) {
return;
}
// We ensure sufficient capacity above so this shouldn't fail.
memcpy(mData, aData, aSize);
mSize = aSize;
}
@@ -515,6 +517,7 @@ MediaRawData::Clone() const
if (!s->EnsureCapacity(mSize)) {
return nullptr;
}
memcpy(s->mData, mData, mSize);
s->mSize = mSize;
}
@@ -535,6 +538,7 @@ MediaRawData::EnsureCapacity(size_t aSize)
if (!newBuffer) {
return false;
}
// Find alignment address.
const uintptr_t alignmask = RAW_DATA_ALIGNMENT;
uint8_t* newData = reinterpret_cast<uint8_t*>(
@@ -557,7 +561,6 @@ size_t
MediaRawData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t size = aMallocSizeOf(this);
size += aMallocSizeOf(mBuffer.get());
return size;
}
@@ -592,6 +595,7 @@ MediaRawDataWriter::SetSize(size_t aSize)
if (aSize > mTarget->mSize && !EnsureSize(aSize)) {
return false;
}
mTarget->mSize = aSize;
return true;
}
@@ -619,6 +623,7 @@ MediaRawDataWriter::Replace(const uint8_t* aData, size_t aSize)
if (!EnsureSize(aSize)) {
return false;
}
memcpy(mTarget->mData, aData, aSize);
mTarget->mSize = aSize;
return true;
+1
View File
@@ -1244,6 +1244,7 @@ MediaFormatReader::Flush(TrackType aTrack)
auto& decoder = GetDecoderData(aTrack);
if (!decoder.mDecoder) {
decoder.ResetState();
return;
}
+37 -11
View File
@@ -299,6 +299,7 @@ MediaSourceTrackDemuxer::MediaSourceTrackDemuxer(MediaSourceDemuxer* aParent,
, mManager(aManager)
, mType(aType)
, mMonitor("MediaSourceTrackDemuxer")
, mReset(true)
{
}
@@ -331,6 +332,8 @@ MediaSourceTrackDemuxer::Reset()
RefPtr<MediaSourceTrackDemuxer> self = this;
nsCOMPtr<nsIRunnable> task =
NS_NewRunnableFunction([self] () {
self->mNextSample.reset();
self->mReset = true;
self->mManager->Seek(self->mType, TimeUnit(), TimeUnit());
{
MonitorAutoLock mon(self->mMonitor);
@@ -388,6 +391,14 @@ MediaSourceTrackDemuxer::DoSeek(media::TimeUnit aTime)
}
TimeUnit seekTime =
mManager->Seek(mType, aTime, MediaSourceDemuxer::EOS_FUZZ);
bool error;
RefPtr<MediaRawData> sample =
mManager->GetSample(mType,
media::TimeUnit(),
error);
MOZ_ASSERT(!error && sample);
mNextSample = Some(sample);
mReset = false;
{
MonitorAutoLock mon(mMonitor);
mNextRandomAccessPoint = mManager->GetNextRandomAccessPoint(mType);
@@ -398,19 +409,34 @@ MediaSourceTrackDemuxer::DoSeek(media::TimeUnit aTime)
RefPtr<MediaSourceTrackDemuxer::SamplesPromise>
MediaSourceTrackDemuxer::DoGetSamples(int32_t aNumSamples)
{
bool error;
RefPtr<MediaRawData> sample =
mManager->GetSample(mType,
MediaSourceDemuxer::EOS_FUZZ,
error);
if (mReset) {
// If a seek (or reset) was recently performed, we ensure that the data
// we are about to retrieve is still available.
TimeIntervals buffered = mManager->Buffered(mType);
buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ);
if (!sample) {
if (error) {
return SamplesPromise::CreateAndReject(DemuxerFailureReason::DEMUXER_ERROR, __func__);
if (!buffered.Contains(TimeUnit::FromMicroseconds(0))) {
return SamplesPromise::CreateAndReject(
mManager->IsEnded() ? DemuxerFailureReason::END_OF_STREAM :
DemuxerFailureReason::WAITING_FOR_DATA, __func__);
}
mReset = false;
}
bool error = false;
RefPtr<MediaRawData> sample;
if (mNextSample) {
sample = mNextSample.ref();
mNextSample.reset();
} else {
sample = mManager->GetSample(mType, MediaSourceDemuxer::EOS_FUZZ, error);
if (!sample) {
if (error) {
return SamplesPromise::CreateAndReject(DemuxerFailureReason::DEMUXER_ERROR, __func__);
}
return SamplesPromise::CreateAndReject(
mManager->IsEnded() ? DemuxerFailureReason::END_OF_STREAM :
DemuxerFailureReason::WAITING_FOR_DATA, __func__);
}
return SamplesPromise::CreateAndReject(
mManager->IsEnded() ? DemuxerFailureReason::END_OF_STREAM :
DemuxerFailureReason::WAITING_FOR_DATA, __func__);
}
RefPtr<SamplesHolder> samples = new SamplesHolder;
samples->mSamples.AppendElement(sample);
@@ -130,6 +130,10 @@ private:
// Monitor protecting members below accessed from multiple threads.
Monitor mMonitor;
media::TimeUnit mNextRandomAccessPoint;
Maybe<RefPtr<MediaRawData>> mNextSample;
// Set to true following a reset. Ensure that the next sample demuxed
// is available at position 0.
bool mReset;
};
} // namespace mozilla
+2
View File
@@ -42,6 +42,8 @@ skip-if = true # bug 1182946
skip-if = true # bug 1182946
[test_HaveMetadataUnbufferedSeek_mp4.html]
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
[test_LoadedDataFired_mp4.html]
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
[test_LiveSeekable.html]
[test_LoadedMetadataFired.html]
skip-if = true # bug 1182946
@@ -0,0 +1,68 @@
<!DOCTYPE HTML>
<html>
<head>
<title>MSE: Check that playback only starts once we have data at time = 0</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="mediasource.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
runWithMSE(function(ms, el) {
el.controls = true;
el.addEventListener("loadeddata", function() {
ok(el.buffered.length > 0, "data is buffered");
is(el.buffered.start(0), 0, "must fire loadeddata when data has been loaded");
is(el.currentTime, 0, "must fire loadeddata at start");
});
el.addEventListener("playing", function() {
ok(el.buffered.length > 0, "data is buffered");
is(el.buffered.start(0), 0, "must fire playing when data has been loaded");
is(el.currentTime, 0, "must fire playing at start");
});
once(ms, 'sourceopen').then(function() {
ok(true, "Receive a sourceopen event");
var videosb = ms.addSourceBuffer("video/mp4");
is(el.readyState, el.HAVE_NOTHING, "readyState is HAVE_NOTHING");
fetchAndLoad(videosb, 'bipbop/bipbop_video', ['init'], '.mp4')
.then(once.bind(null, el, "loadedmetadata"))
.then(function() {
videosb.appendWindowStart = 2;
videosb.appendWindowEnd = 4;
is(el.readyState, el.HAVE_METADATA, "readyState is HAVE_METADATA");
// Load [2.4, 3.968344). 2.4 as it's the first keyframe after 2s and
// 3.968344 as the last frame ends after 4s.
return fetchAndLoad(videosb, 'bipbop/bipbop_video', range(1, 8), '.m4s');
})
.then(function() {
is(el.readyState, el.HAVE_METADATA, "readyState is HAVE_METADATA");
// test that appendWindowEnd did its job.
ok(el.buffered.start(0) >= 2, "no data can be found prior appendWindowStart");
ok(el.buffered.end(el.buffered.length-1) <= 4, "no data can be found beyond appendWindowEnd");
el.play();
return once(el, "play");
})
.then(function() {
videosb.appendWindowStart = 0;
var promises = [];
// Load [0, 3.971666).
promises.push(fetchAndLoad(videosb, 'bipbop/bipbop_video', range(1, 8), '.m4s'));
// playback can only start now.
promises.push(once(el, "playing"));
return Promise.all(promises);
})
.then(function() {
ok(true, "playing");
SimpleTest.finish();
});
});
});
</script>
</pre>
</body>
</html>
+10 -2
View File
@@ -21,10 +21,18 @@ enum HitTestResult {
};
enum CancelAnimationFlags : uint32_t {
Default = 0, /* Cancel all animations */
ExcludeOverscroll = 1 /* Don't clear overscroll */
Default = 0x0, /* Cancel all animations */
ExcludeOverscroll = 0x1, /* Don't clear overscroll */
RequestSnap = 0x2 /* Request snapping to snap points */
};
inline CancelAnimationFlags
operator|(CancelAnimationFlags a, CancelAnimationFlags b)
{
return static_cast<CancelAnimationFlags>(static_cast<int>(a)
| static_cast<int>(b));
}
enum class ScrollSource {
// scrollTo() or something similar.
DOM,
+102 -68
View File
@@ -139,9 +139,6 @@ using mozilla::gfx::PointTyped;
* events we dispatched to it.\n
* Units: milliseconds
*
* \li\b apz.cross_slide_enabled
* Pref that enables integration with the Metro "cross-slide" gesture.
*
* \li\b apz.danger_zone_x
* \li\b apz.danger_zone_y
* When drawing high-res tiles, we drop down to drawing low-res tiles
@@ -201,8 +198,11 @@ using mozilla::gfx::PointTyped;
* http://mxr.mozilla.org/mozilla-central/source/layout/style/nsStyleStruct.cpp?rev=21282be9ad95#2462
*
* \li\b apz.fling_friction
* Amount of friction applied during flings.
*
* Amount of friction applied during flings. This is used in the following
* formula: v(t1) = v(t0) * (1 - f)^(t1 - t0), where v(t1) is the velocity
* for a new sample, v(t0) is the velocity at the previous sample, f is the
* value of this pref, and (t1 - t0) is the amount of time, in milliseconds,
* that has elapsed between the two samples.
*
* \li\b apz.fling_repaint_interval
* Maximum amount of time flinging before sending a viewport change. This will
@@ -684,7 +684,14 @@ public:
bool continueX = mApzc.mX.SampleOverscrollAnimation(aDelta);
bool continueY = mApzc.mY.SampleOverscrollAnimation(aDelta);
if (!continueX && !continueY) {
mApzc.OverscrollAnimationEnding();
// If we got into overscroll from a fling, that fling did not request a
// fling snap to avoid a resulting scrollTo from cancelling the overscroll
// animation too early. We do still want to request a fling snap, though,
// in case the end of the axis at which we're overscrolled is not a valid
// snap point, so we request one now. If there are no snap points, this will
// do nothing. If there are snap points, we'll get a scrollTo that snaps us
// back to the nearest valid snap point.
mApzc.RequestSnap();
return false;
}
return true;
@@ -908,7 +915,7 @@ AsyncPanZoomController::Destroy()
{
APZThreadUtils::AssertOnCompositorThread();
CancelAnimation();
CancelAnimation(CancelAnimationFlags::RequestSnap);
{ // scope the lock
MonitorAutoLock lock(mRefPtrMonitor);
@@ -1032,8 +1039,8 @@ nsEventStatus AsyncPanZoomController::HandleDragEvent(const MouseInput& aEvent,
CSSPoint scrollFramePoint = aEvent.mLocalOrigin / GetFrameMetrics().GetZoom();
// The scrollbar can be transformed with the frame but the pres shell
// resolution is only applied to the scroll frame.
CSSPoint scrollbarPoint = scrollFramePoint * GetFrameMetrics().GetPresShellResolution();
CSSRect cssCompositionBound = GetFrameMetrics().GetCompositionBounds() / GetFrameMetrics().GetZoom();
CSSPoint scrollbarPoint = scrollFramePoint * mFrameMetrics.GetPresShellResolution();
CSSRect cssCompositionBound = mFrameMetrics.CalculateCompositedRectInCssPixels();
float mousePosition = GetAxisStart(aDragMetrics.mDirection, scrollbarPoint) -
aDragMetrics.mScrollbarDragOffset -
@@ -1042,34 +1049,31 @@ nsEventStatus AsyncPanZoomController::HandleDragEvent(const MouseInput& aEvent,
float scrollMax = GetAxisEnd(aDragMetrics.mDirection, aDragMetrics.mScrollTrack);
scrollMax -= node->GetScrollSize() /
GetAxisScale(aDragMetrics.mDirection, GetFrameMetrics().GetZoom()) *
GetFrameMetrics().GetPresShellResolution();
GetAxisScale(aDragMetrics.mDirection, mFrameMetrics.GetZoom()) *
mFrameMetrics.GetPresShellResolution();
float scrollPercent = mousePosition / scrollMax;
float minScrollPosition =
GetAxisStart(aDragMetrics.mDirection, GetFrameMetrics().GetScrollableRect().TopLeft());
GetAxisStart(aDragMetrics.mDirection, mFrameMetrics.GetScrollableRect().TopLeft());
float maxScrollPosition =
GetAxisSize(aDragMetrics.mDirection, GetFrameMetrics().GetScrollableRect()) -
GetAxisSize(aDragMetrics.mDirection, GetFrameMetrics().GetCompositionBounds());
GetAxisSize(aDragMetrics.mDirection, mFrameMetrics.GetScrollableRect()) -
GetAxisSize(aDragMetrics.mDirection, mFrameMetrics.GetCompositionBounds());
float scrollPosition = scrollPercent * maxScrollPosition;
scrollPosition = std::max(scrollPosition, minScrollPosition);
scrollPosition = std::min(scrollPosition, maxScrollPosition);
CSSPoint scrollOffset;
CSSPoint scrollOffset = mFrameMetrics.GetScrollOffset();
if (aDragMetrics.mDirection == AsyncDragMetrics::HORIZONTAL) {
scrollOffset = CSSPoint(scrollPosition, 0);
scrollOffset.x = scrollPosition;
} else {
scrollOffset = CSSPoint(0, scrollPosition);
scrollOffset.y = scrollPosition;
}
mFrameMetrics.SetScrollOffset(scrollOffset);
ScheduleCompositeAndMaybeRepaint();
UpdateSharedCompositorFrameMetrics();
// Here we consume the events. This means that the content scrollbars
// will only see the initial mouse down and the final mouse up.
// APZ will still update the scroll position.
return nsEventStatus_eConsumeNoDefault;
}
@@ -1123,13 +1127,13 @@ nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent,
break;
}
case MOUSE_INPUT: {
ScrollWheelInput scrollInput = aEvent.AsScrollWheelInput();
if (!scrollInput.TransformToLocal(aTransformToApzc)) {
MouseInput mouseInput = aEvent.AsMouseInput();
if (!mouseInput.TransformToLocal(aTransformToApzc)) {
return rv;
}
// TODO Need to implement blocks to properly handle this.
//rv = HandleDragEvent(scrollInput, dragMetrics);
//rv = HandleDragEvent(mouseInput, dragMetrics);
break;
}
case SCROLLWHEEL_INPUT: {
@@ -1230,8 +1234,6 @@ nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent
case PANNING:
case PANNING_LOCKED_X:
case PANNING_LOCKED_Y:
case CROSS_SLIDING_X:
case CROSS_SLIDING_Y:
case PINCHING:
NS_WARNING("Received impossible touch in OnTouchStart");
break;
@@ -1254,12 +1256,6 @@ nsEventStatus AsyncPanZoomController::OnTouchMove(const MultiTouchInput& aEvent)
// second tap. Ignore the move if this happens.
return nsEventStatus_eIgnore;
case CROSS_SLIDING_X:
case CROSS_SLIDING_Y:
// While cross-sliding, we don't want to consume any touchmove events for
// panning or zooming, and let the caller handle them instead.
return nsEventStatus_eIgnore;
case TOUCHING: {
ScreenCoord panThreshold = GetTouchStartTolerance();
UpdateWithTouchAtDevicePoint(aEvent);
@@ -1331,16 +1327,22 @@ nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent)
return nsEventStatus_eIgnore;
case TOUCHING:
case CROSS_SLIDING_X:
case CROSS_SLIDING_Y:
// We may have some velocity stored on the axis from move events
// that were not big enough to trigger scrolling. Clear that out.
mX.SetVelocity(0);
mY.SetVelocity(0);
// It's possible we may be overscrolled if the user tapped during a
// previous overscroll pan. Make sure to snap back in this situation.
if (!SnapBackIfOverscrolled()) {
SetState(NOTHING);
APZC_LOG("%p still has %u touch points active\n", this,
CurrentTouchBlock()->GetActiveTouchCount());
// In cases where the user is panning, then taps the second finger without
// entering a pinch, we will arrive here when the second finger is lifted.
// However the first finger is still down so we want to remain in state
// TOUCHING.
if (CurrentTouchBlock()->GetActiveTouchCount() == 0) {
// It's possible we may be overscrolled if the user tapped during a
// previous overscroll pan. Make sure to snap back in this situation.
if (!SnapBackIfOverscrolled()) {
SetState(NOTHING);
}
}
return nsEventStatus_eIgnore;
@@ -1535,6 +1537,10 @@ nsEventStatus AsyncPanZoomController::OnScaleEnd(const PinchGestureInput& aEvent
} else {
ClearOverscroll();
}
// Along with clearing the overscroll, we also want to snap to the nearest
// snap point as appropriate, so ask the main thread (which knows about such
// things) to handle it.
RequestSnap();
ScheduleComposite();
RequestContentRepaint();
@@ -1749,12 +1755,9 @@ nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEve
switch (aEvent.mScrollMode) {
case ScrollWheelInput::SCROLLMODE_INSTANT: {
// Call ToScreenCoordinates outside the lock because it grabs the tree lock.
ScreenPoint distance = ToScreenCoordinates(
ParentLayerPoint(fabs(delta.x), fabs(delta.y)), aEvent.mLocalOrigin);
ReentrantMonitorAutoEnter lock(mMonitor);
CancelAnimation();
SetState(WHEEL_SCROLL);
@@ -1767,6 +1770,10 @@ nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEve
CallDispatchScroll(startPoint, endPoint, handoffState);
SetState(NOTHING);
// The calls above handle their own locking; moreover,
// ToScreenCoordinates() and CallDispatchScroll() can grab the tree lock.
ReentrantMonitorAutoEnter lock(mMonitor);
RequestContentRepaint();
break;
@@ -1933,6 +1940,19 @@ nsEventStatus AsyncPanZoomController::OnPanEnd(const PanGestureInput& aEvent) {
mX.EndTouch(aEvent.mTime);
mY.EndTouch(aEvent.mTime);
// Drop any velocity on axes where we don't have room to scroll anyways
// (in this APZC, or an APZC further in the handoff chain).
// This ensures that we don't enlarge the display port unnecessarily.
RefPtr<const OverscrollHandoffChain> overscrollHandoffChain =
CurrentPanGestureBlock()->GetOverscrollHandoffChain();
if (!overscrollHandoffChain->CanScrollInDirection(this, Layer::HORIZONTAL)) {
mX.SetVelocity(0);
}
if (!overscrollHandoffChain->CanScrollInDirection(this, Layer::VERTICAL)) {
mY.SetVelocity(0);
}
SetState(NOTHING);
RequestContentRepaint();
@@ -2182,24 +2202,17 @@ void AsyncPanZoomController::HandlePanning(double aAngle) {
bool canScrollVertical = !mY.IsAxisLocked() &&
overscrollHandoffChain->CanScrollInDirection(this, Layer::VERTICAL);
if (!gfxPrefs::APZCrossSlideEnabled() &&
(!canScrollHorizontal || !canScrollVertical)) {
if (!canScrollHorizontal || !canScrollVertical) {
SetState(PANNING);
} else if (IsCloseToHorizontal(aAngle, gfxPrefs::APZAxisLockAngle())) {
mY.SetAxisLocked(true);
if (canScrollHorizontal) {
SetState(PANNING_LOCKED_X);
} else {
SetState(CROSS_SLIDING_X);
mX.SetAxisLocked(true);
}
} else if (IsCloseToVertical(aAngle, gfxPrefs::APZAxisLockAngle())) {
mX.SetAxisLocked(true);
if (canScrollVertical) {
SetState(PANNING_LOCKED_Y);
} else {
SetState(CROSS_SLIDING_Y);
mY.SetAxisLocked(true);
}
} else {
SetState(PANNING);
@@ -2216,13 +2229,13 @@ void AsyncPanZoomController::HandlePanningUpdate(const ScreenPoint& aPanDistance
float breakThreshold = gfxPrefs::APZAxisBreakoutThreshold() * APZCTreeManager::GetDPI();
if (fabs(aPanDistance.x) > breakThreshold || fabs(aPanDistance.y) > breakThreshold) {
if (mState == PANNING_LOCKED_X || mState == CROSS_SLIDING_X) {
if (mState == PANNING_LOCKED_X) {
if (!IsCloseToHorizontal(angle, gfxPrefs::APZAxisBreakoutAngle())) {
mY.SetAxisLocked(false);
SetState(PANNING);
}
} else if (mState == PANNING_LOCKED_Y || mState == CROSS_SLIDING_Y) {
if (!IsCloseToVertical(angle, gfxPrefs::APZAxisLockAngle())) {
} else if (mState == PANNING_LOCKED_Y) {
if (!IsCloseToVertical(angle, gfxPrefs::APZAxisBreakoutAngle())) {
mX.SetAxisLocked(false);
SetState(PANNING);
}
@@ -2568,6 +2581,8 @@ void AsyncPanZoomController::CancelAnimation(CancelAnimationFlags aFlags) {
bool repaint = !IsZero(GetVelocityVector());
mX.SetVelocity(0);
mY.SetVelocity(0);
mX.SetAxisLocked(false);
mY.SetAxisLocked(false);
// Setting the state to nothing and cancelling the animation can
// preempt normal mechanisms for relieving overscroll, so we need to clear
// overscroll here.
@@ -2575,6 +2590,11 @@ void AsyncPanZoomController::CancelAnimation(CancelAnimationFlags aFlags) {
ClearOverscroll();
repaint = true;
}
// Similar to relieving overscroll, we also need to snap to any snap points
// if appropriate, so ask the main thread to do that.
if (aFlags & CancelAnimationFlags::RequestSnap) {
RequestSnap();
}
if (repaint) {
RequestContentRepaint();
ScheduleComposite();
@@ -2617,15 +2637,20 @@ static CSSSize
CalculateDisplayPortSize(const CSSSize& aCompositionSize,
const CSSPoint& aVelocity)
{
float xMultiplier = fabsf(aVelocity.x) < gfxPrefs::APZMinSkateSpeed()
bool xIsStationarySpeed = fabsf(aVelocity.x) < gfxPrefs::APZMinSkateSpeed();
bool yIsStationarySpeed = fabsf(aVelocity.y) < gfxPrefs::APZMinSkateSpeed();
float xMultiplier = xIsStationarySpeed
? gfxPrefs::APZXStationarySizeMultiplier()
: gfxPrefs::APZXSkateSizeMultiplier();
float yMultiplier = fabsf(aVelocity.y) < gfxPrefs::APZMinSkateSpeed()
float yMultiplier = yIsStationarySpeed
? gfxPrefs::APZYStationarySizeMultiplier()
: gfxPrefs::APZYSkateSizeMultiplier();
if (IsHighMemSystem()) {
if (IsHighMemSystem() && !xIsStationarySpeed) {
xMultiplier += gfxPrefs::APZXSkateHighMemAdjust();
}
if (IsHighMemSystem() && !yIsStationarySpeed) {
yMultiplier += gfxPrefs::APZYSkateHighMemAdjust();
}
@@ -2745,6 +2770,12 @@ bool AsyncPanZoomController::SnapBackIfOverscrolled() {
StartOverscrollAnimation(ParentLayerPoint(0, 0));
return true;
}
// If we don't kick off an overscroll animation, we still need to ask the
// main thread to snap to any nearby snap points, assuming we haven't already
// done so when we started this fling
if (mState != FLING) {
RequestSnap();
}
return false;
}
@@ -3314,13 +3345,16 @@ void AsyncPanZoomController::ZoomToRect(CSSRect aRect) {
targetZoom = CSSToParentLayerScale(std::min(compositionBounds.width / aRect.width,
compositionBounds.height / aRect.height));
}
// 1. If the rect is empty, request received from browserElementScrolling.js
// 1. If the rect is empty, the content-side logic for handling a double-tap
// requested that we zoom out.
// 2. currentZoom is equal to mZoomConstraints.mMaxZoom and user still double-tapping it
// 3. currentZoom is equal to localMinZoom and user still double-tapping it
// Treat these three cases as a request to zoom out as much as possible.
bool zoomOut = false;
if (aRect.IsEmpty() ||
(currentZoom == localMaxZoom && targetZoom >= localMaxZoom) ||
(currentZoom == localMinZoom && targetZoom <= localMinZoom)) {
zoomOut = true;
CSSSize compositedSize = mFrameMetrics.CalculateCompositedSizeInCssPixels();
float y = scrollOffset.y;
float newHeight =
@@ -3354,6 +3388,14 @@ void AsyncPanZoomController::ZoomToRect(CSSRect aRect) {
aRect.x = aRect.x > 0 ? aRect.x : 0;
}
// Vertically center the zoomed element in the screen.
if (!zoomOut && (sizeAfterZoom.height > aRect.height)) {
aRect.y -= (sizeAfterZoom.height - aRect.height) * 0.5f;
if (aRect.y < 0.0f) {
aRect.y = 0.0f;
}
}
endZoomToMetrics.SetScrollOffset(aRect.TopLeft());
endZoomToMetrics.SetDisplayPortMargins(
CalculatePendingDisplayPort(endZoomToMetrics, ParentLayerPoint(0,0)));
@@ -3390,11 +3432,8 @@ AsyncPanZoomController::CurrentPanGestureBlock() const
}
void
AsyncPanZoomController::ResetInputState()
AsyncPanZoomController::ResetTouchInputState()
{
// This may be called during non-touch input blocks as well. We send
// a fake cancel touch event here but on the assumption that none of the
// code in GEL assumes a CurrentTouchBlock()
MultiTouchInput cancel(MultiTouchInput::MULTITOUCH_CANCEL, 0, TimeStamp::Now(), 0);
RefPtr<GestureEventListener> listener = GetGestureEventListener();
if (listener) {
@@ -3408,7 +3447,7 @@ AsyncPanZoomController::CancelAnimationAndGestureState()
{
mX.CancelGesture();
mY.CancelGesture();
CancelAnimation();
CancelAnimation(CancelAnimationFlags::RequestSnap);
}
bool
@@ -3573,15 +3612,10 @@ void AsyncPanZoomController::ShareCompositorFrameMetrics() {
}
}
void AsyncPanZoomController::OverscrollAnimationEnding() {
// If we got into overscroll from a fling, that fling did not request a
// fling snap to avoid a resulting scrollTo from cancelling the overscroll
// animation too early. We do still want to request a fling snap, though,
// in case the end of the axis at which we're overscrolled is not a valid
// snap point, so we request one now. If there are no snap points, this will
// do nothing. If there are snap points, we'll get a scrollTo that snaps us
// back to the nearest valid snap point.
void AsyncPanZoomController::RequestSnap() {
if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
APZC_LOG("%p requesting snap near %s\n", this,
Stringify(mFrameMetrics.GetScrollOffset()).c_str());
controller->RequestFlingSnap(mFrameMetrics.GetScrollId(),
mFrameMetrics.GetScrollOffset());
}
+6 -10
View File
@@ -636,9 +636,10 @@ protected:
// Common processing at the end of a touch block.
void OnTouchEndOrCancel();
// This is called by OverscrollAnimation to notify us when the overscroll
// animation is ending.
void OverscrollAnimationEnding();
// This is called to request that the main thread snap the scroll position
// to a nearby snap position if appropriate. The current scroll position is
// used as the final destination.
void RequestSnap();
uint64_t mLayersId;
RefPtr<CompositorParent> mCompositorParent;
@@ -741,11 +742,6 @@ protected:
PAN_MOMENTUM, /* like PANNING, but controlled by momentum PanGestureInput events */
CROSS_SLIDING_X, /* Panning disabled while user does a horizontal gesture
on a vertically-scrollable view. This used for the
Windows Metro "cross-slide" gesture. */
CROSS_SLIDING_Y, /* as above for Y axis */
PINCHING, /* nth touch-start, where n > 1. this mode allows pan and zoom */
ANIMATING_ZOOM, /* animated zoom to a new rect */
OVERSCROLL_ANIMATION, /* Spring-based animation used to relieve overscroll once
@@ -805,9 +801,9 @@ public:
bool ArePointerEventsConsumable(TouchBlockState* aBlock, uint32_t aTouchPoints);
/**
* Clear internal state relating to input handling.
* Clear internal state relating to touch input handling.
*/
void ResetInputState();
void ResetTouchInputState();
private:
void CancelAnimationAndGestureState();
+2 -1
View File
@@ -76,7 +76,7 @@ void Axis::UpdateWithTouchAtDevicePoint(ParentLayerCoord aPos, ParentLayerCoord
return;
}
float newVelocity = mAxisLocked ? 0.0f : (float)(mPos - aPos - aAdditionalDelta) / (float)(aTimestampMs - mPosTimeMs);
float newVelocity = mAxisLocked ? 0.0f : (float)(mPos - aPos + aAdditionalDelta) / (float)(aTimestampMs - mPosTimeMs);
if (gfxPrefs::APZMaxVelocity() > 0.0f) {
bool velocityIsNegative = (newVelocity < 0);
newVelocity = fabs(newVelocity);
@@ -382,6 +382,7 @@ void Axis::EndTouch(uint32_t aTimestampMs) {
// mVelocityQueue is controller-thread only
APZThreadUtils::AssertOnControllerThread();
mAxisLocked = false;
mVelocity = 0;
int count = 0;
while (!mVelocityQueue.IsEmpty()) {
+6
View File
@@ -887,5 +887,11 @@ TouchBlockState::UpdateSlopState(const MultiTouchInput& aInput,
return mInSlop;
}
uint32_t
TouchBlockState::GetActiveTouchCount() const
{
return mTouchCounter.GetActiveTouchCount();
}
} // namespace layers
} // namespace mozilla
+5
View File
@@ -453,6 +453,11 @@ public:
bool UpdateSlopState(const MultiTouchInput& aInput,
bool aApzcCanConsumeEvents);
/**
* Returns the number of touch points currently active.
*/
uint32_t GetActiveTouchCount() const;
bool HasEvents() const override;
void DropEvents() override;
void HandleEvents() override;
+4 -2
View File
@@ -661,7 +661,9 @@ InputQueue::ProcessInputBlocks() {
curBlock->DropEvents();
} else if (curBlock->IsDefaultPrevented()) {
curBlock->DropEvents();
target->ResetInputState();
if (curBlock->AsTouchBlock()) {
target->ResetTouchInputState();
}
} else {
UpdateActiveApzc(curBlock->GetTargetApzc());
curBlock->HandleEvents();
@@ -687,7 +689,7 @@ void
InputQueue::UpdateActiveApzc(const RefPtr<AsyncPanZoomController>& aNewActive) {
if (mLastActiveApzc && mLastActiveApzc != aNewActive
&& mTouchCounter.GetActiveTouchCount() > 0) {
mLastActiveApzc->ResetInputState();
mLastActiveApzc->ResetTouchInputState();
}
mLastActiveApzc = aNewActive;
}
@@ -917,6 +917,81 @@ TEST_F(APZCPinchGestureDetectorTester, Pinch_PreventDefault) {
apzc->AssertStateIsReset();
}
TEST_F(APZCGestureDetectorTester, Pan_With_Tap) {
SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
FrameMetrics originalMetrics = GetPinchableFrameMetrics();
apzc->SetFrameMetrics(originalMetrics);
// Making the APZC zoomable isn't really needed for the correct operation of
// this test, but it could help catch regressions where we accidentally enter
// a pinch state.
MakeApzcZoomable();
// Test parameters
int touchX = 250;
int touchY = 300;
int panDistance = 20;
int firstFingerId = 0;
int secondFingerId = firstFingerId + 1;
// Put finger down
MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
apzc->ReceiveInputEvent(mti, nullptr);
// Start a pan, break through the threshold
touchY += 40;
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
apzc->ReceiveInputEvent(mti, nullptr);
// Do an actual pan for a bit
touchY += panDistance;
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
apzc->ReceiveInputEvent(mti, nullptr);
// Put a second finger down
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
mti.mTouches.AppendElement(CreateSingleTouchData(secondFingerId, touchX + 10, touchY));
apzc->ReceiveInputEvent(mti, nullptr);
// Lift the second finger
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
mti.mTouches.AppendElement(CreateSingleTouchData(secondFingerId, touchX + 10, touchY));
apzc->ReceiveInputEvent(mti, nullptr);
// Bust through the threshold again
touchY += 40;
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
apzc->ReceiveInputEvent(mti, nullptr);
// Do some more actual panning
touchY += panDistance;
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
apzc->ReceiveInputEvent(mti, nullptr);
// Lift the first finger
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
apzc->ReceiveInputEvent(mti, nullptr);
// Verify that we scrolled
FrameMetrics finalMetrics = apzc->GetFrameMetrics();
float zoom = finalMetrics.GetZoom().ToScaleFactor().scale;
EXPECT_EQ(originalMetrics.GetScrollOffset().y - (panDistance * 2 / zoom), finalMetrics.GetScrollOffset().y);
// Clear out any remaining fling animation and pending tasks
apzc->AdvanceAnimationsUntilEnd();
while (mcc->RunThroughDelayedTasks());
apzc->AssertStateIsReset();
}
TEST_F(APZCBasicTester, Overzoom) {
// the visible area of the document in CSS pixels is x=10 y=0 w=100 h=100
FrameMetrics fm;
-1
View File
@@ -146,7 +146,6 @@ private:
DECL_GFX_PREF(Live, "apz.axis_lock.lock_angle", APZAxisLockAngle, float, float(M_PI / 6.0) /* 30 degrees */);
DECL_GFX_PREF(Live, "apz.axis_lock.mode", APZAxisLockMode, int32_t, 0);
DECL_GFX_PREF(Live, "apz.content_response_timeout", APZContentResponseTimeout, int32_t, 300);
DECL_GFX_PREF(Live, "apz.cross_slide.enabled", APZCrossSlideEnabled, bool, false);
DECL_GFX_PREF(Live, "apz.danger_zone_x", APZDangerZoneX, int32_t, 50);
DECL_GFX_PREF(Live, "apz.danger_zone_y", APZDangerZoneY, int32_t, 100);
DECL_GFX_PREF(Live, "apz.drag.enabled", APZDragEnabled, bool, false);
-1
View File
@@ -595,7 +595,6 @@ pref("apz.axis_lock.breakout_threshold", "0.03125"); // 1/32 inches
pref("apz.axis_lock.breakout_angle", "0.3926991"); // PI / 8 (22.5 degrees)
pref("apz.axis_lock.direct_pan_angle", "1.047197"); // PI / 3 (60 degrees)
pref("apz.content_response_timeout", 300);
pref("apz.cross_slide.enabled", false);
pref("apz.drag.enabled", false);
pref("apz.danger_zone_x", 50);
pref("apz.danger_zone_y", 100);