diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp
index 83c68861bb..79912107b5 100644
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -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());
diff --git a/dom/html/nsTextEditorState.cpp b/dom/html/nsTextEditorState.cpp
index b4ab7e1647..8f472a9d6e 100644
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -110,7 +110,10 @@ public:
}
mTextEditorState->mSelectionRestoreEagerInit = false;
}
- mTextEditorState->FinishedRestoringSelection();
+
+ if (mTextEditorState) {
+ mTextEditorState->FinishedRestoringSelection();
+ }
return NS_OK;
}
diff --git a/dom/html/nsTextEditorState.h b/dom/html/nsTextEditorState.h
index a64caee183..f2e5e83b45 100644
--- a/dom/html/nsTextEditorState.h
+++ b/dom/html/nsTextEditorState.h
@@ -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 mSelCon;
RefPtr mRestoringSelection;
nsCOMPtr mEditor;
diff --git a/dom/media/MediaData.cpp b/dom/media/MediaData.cpp
index dbbe172020..80ebc3a4b1 100644
--- a/dom/media/MediaData.cpp
+++ b/dom/media/MediaData.cpp
@@ -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(
@@ -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;
diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp
index b29a3de4e5..085686d37b 100644
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -1244,6 +1244,7 @@ MediaFormatReader::Flush(TrackType aTrack)
auto& decoder = GetDecoderData(aTrack);
if (!decoder.mDecoder) {
+ decoder.ResetState();
return;
}
diff --git a/dom/media/mediasource/MediaSourceDemuxer.cpp b/dom/media/mediasource/MediaSourceDemuxer.cpp
index 4de3922b40..8833819eb1 100644
--- a/dom/media/mediasource/MediaSourceDemuxer.cpp
+++ b/dom/media/mediasource/MediaSourceDemuxer.cpp
@@ -299,6 +299,7 @@ MediaSourceTrackDemuxer::MediaSourceTrackDemuxer(MediaSourceDemuxer* aParent,
, mManager(aManager)
, mType(aType)
, mMonitor("MediaSourceTrackDemuxer")
+ , mReset(true)
{
}
@@ -331,6 +332,8 @@ MediaSourceTrackDemuxer::Reset()
RefPtr self = this;
nsCOMPtr 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 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::DoGetSamples(int32_t aNumSamples)
{
- bool error;
- RefPtr 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 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 samples = new SamplesHolder;
samples->mSamples.AppendElement(sample);
diff --git a/dom/media/mediasource/MediaSourceDemuxer.h b/dom/media/mediasource/MediaSourceDemuxer.h
index 2fe28378b3..033d8af32d 100644
--- a/dom/media/mediasource/MediaSourceDemuxer.h
+++ b/dom/media/mediasource/MediaSourceDemuxer.h
@@ -130,6 +130,10 @@ private:
// Monitor protecting members below accessed from multiple threads.
Monitor mMonitor;
media::TimeUnit mNextRandomAccessPoint;
+ Maybe> mNextSample;
+ // Set to true following a reset. Ensure that the next sample demuxed
+ // is available at position 0.
+ bool mReset;
};
} // namespace mozilla
diff --git a/dom/media/mediasource/test/mochitest.ini b/dom/media/mediasource/test/mochitest.ini
index adf77558c4..9fed5e0047 100644
--- a/dom/media/mediasource/test/mochitest.ini
+++ b/dom/media/mediasource/test/mochitest.ini
@@ -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
diff --git a/dom/media/mediasource/test/test_LoadedDataFired_mp4.html b/dom/media/mediasource/test/test_LoadedDataFired_mp4.html
new file mode 100644
index 0000000000..5af5d51a90
--- /dev/null
+++ b/dom/media/mediasource/test/test_LoadedDataFired_mp4.html
@@ -0,0 +1,68 @@
+
+
+
+ MSE: Check that playback only starts once we have data at time = 0
+
+
+
+
+
+
+
+
+
+
diff --git a/gfx/layers/apz/src/APZUtils.h b/gfx/layers/apz/src/APZUtils.h
index ad8d157371..a85fddc03e 100644
--- a/gfx/layers/apz/src/APZUtils.h
+++ b/gfx/layers/apz/src/APZUtils.h
@@ -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(static_cast(a)
+ | static_cast(b));
+}
+
enum class ScrollSource {
// scrollTo() or something similar.
DOM,
diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp
index ad7f9f95ca..a4424c13e4 100644
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -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 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 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 controller = GetGeckoContentController()) {
+ APZC_LOG("%p requesting snap near %s\n", this,
+ Stringify(mFrameMetrics.GetScrollOffset()).c_str());
controller->RequestFlingSnap(mFrameMetrics.GetScrollId(),
mFrameMetrics.GetScrollOffset());
}
diff --git a/gfx/layers/apz/src/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h
index a339a7b8cd..ad968c878b 100644
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -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 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();
diff --git a/gfx/layers/apz/src/Axis.cpp b/gfx/layers/apz/src/Axis.cpp
index e02e336270..8b9a0cd111 100644
--- a/gfx/layers/apz/src/Axis.cpp
+++ b/gfx/layers/apz/src/Axis.cpp
@@ -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()) {
diff --git a/gfx/layers/apz/src/InputBlockState.cpp b/gfx/layers/apz/src/InputBlockState.cpp
index 0d8725ed6c..924ed7cfff 100644
--- a/gfx/layers/apz/src/InputBlockState.cpp
+++ b/gfx/layers/apz/src/InputBlockState.cpp
@@ -887,5 +887,11 @@ TouchBlockState::UpdateSlopState(const MultiTouchInput& aInput,
return mInSlop;
}
+uint32_t
+TouchBlockState::GetActiveTouchCount() const
+{
+ return mTouchCounter.GetActiveTouchCount();
+}
+
} // namespace layers
} // namespace mozilla
diff --git a/gfx/layers/apz/src/InputBlockState.h b/gfx/layers/apz/src/InputBlockState.h
index 14ba191c69..98a8bb9cb8 100644
--- a/gfx/layers/apz/src/InputBlockState.h
+++ b/gfx/layers/apz/src/InputBlockState.h
@@ -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;
diff --git a/gfx/layers/apz/src/InputQueue.cpp b/gfx/layers/apz/src/InputQueue.cpp
index 6b04bf3d6a..7b62a7ff00 100644
--- a/gfx/layers/apz/src/InputQueue.cpp
+++ b/gfx/layers/apz/src/InputQueue.cpp
@@ -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& aNewActive) {
if (mLastActiveApzc && mLastActiveApzc != aNewActive
&& mTouchCounter.GetActiveTouchCount() > 0) {
- mLastActiveApzc->ResetInputState();
+ mLastActiveApzc->ResetTouchInputState();
}
mLastActiveApzc = aNewActive;
}
diff --git a/gfx/layers/apz/test/gtest/TestAsyncPanZoomController.cpp b/gfx/layers/apz/test/gtest/TestAsyncPanZoomController.cpp
index 1c1173f5f5..d6179837a3 100644
--- a/gfx/layers/apz/test/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/layers/apz/test/gtest/TestAsyncPanZoomController.cpp
@@ -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;
diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h
index ea161cc082..f73bb05648 100644
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -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);
diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
index c37b074700..b89e9fabf0 100644
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -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);