Bug 1238761 - Implement NativePanZoomController native methods; r=rbarker

This patch adds the NPZCSupport class to nsWindow and use it to
implement the NPZC native methods that were implemented in
AndroidJNI.cpp. For HandleMotionEvent, the code also includes a portion
from AndroidJavaWrapper::MakeMultitouchEvent.
This commit is contained in:
Jim Chen 2016-01-15 13:05:45 -05:00
parent 296a0a08cc
commit 919de6549b
2 changed files with 286 additions and 12 deletions

View File

@ -154,6 +154,7 @@ static bool sFailedToCreateGLContext = false;
static const double SWIPE_MAX_PINCH_DELTA_INCHES = 0.4;
static const double SWIPE_MIN_DISTANCE_INCHES = 0.6;
static Modifiers GetModifiers(int32_t metaState);
class nsWindow::GeckoViewSupport final
: public GeckoView::Window::Natives<GeckoViewSupport>
@ -245,17 +246,12 @@ public:
, mIMESelectionChanged(false)
, mIMETextChangedDuringFlush(false)
{
Reattach(aInstance);
Base::AttachNative(aInstance, this);
EditableBase::AttachNative(mEditable, this);
}
~GeckoViewSupport();
void Reattach(const GeckoView::Window::LocalRef& aInstance)
{
Base::AttachNative(aInstance, this);
}
using Base::DisposeNative;
using EditableBase::DisposeNative;
@ -367,6 +363,282 @@ public:
void OnImeUpdateComposition(int32_t aStart, int32_t aEnd);
};
/**
* NativePanZoomController handles its native calls on the UI thread, so make
* it separate from GeckoViewSupport.
*/
class nsWindow::NPZCSupport final
: public NativePanZoomController::Natives<NPZCSupport>
{
nsWindow* mWindow;
// Lock for keeping mWindow alive when accessed off of the Gecko thread.
Mutex mWindowLock;
NativePanZoomController::GlobalRef mNPZC;
public:
typedef NativePanZoomController::Natives<NPZCSupport> Base;
NPZCSupport(nsWindow* aWindow,
const NativePanZoomController::LocalRef& aNPZC)
: mWindow(aWindow)
, mWindowLock("NPZCSupport")
, mNPZC(aNPZC)
{
if (mWindow->mNPZCSupport) {
mWindow->mNPZCSupport->DetachFromWindow();
}
mWindow->mNPZCSupport = this;
}
~NPZCSupport()
{}
using Base::AttachNative;
void DetachFromWindow()
{
// There are several considerations when shutting down NPZC. 1) The
// Gecko thread may destroy NPZC at any time when nsWindow closes. 2)
// There may be pending events on the Gecko thread when NPZC is
// destroyed. 3) mWindow may not be available when the pending event
// runs. 4) The UI thread may destroy NPZC at any time when GeckoView
// is destroyed. 5) The UI thread may destroy NPZC at the same time as
// Gecko thread trying to destroy NPZC. 6) There may be pending calls
// on the UI thread when NPZC is destroyed. 7) mWindow may have been
// cleared on the Gecko thread when the pending call happens on the UI
// thread.
//
// 1) happens through DetachFromWindow, which first notifies the UI
// thread through Destroy; Destroy then calls DisposeNative, which
// finally disposes the native instance back on the Gecko thread. Using
// Destroy to indirectly call DisposeNative here also solves 5), by
// making everything go through the UI thread, avoiding contention.
//
// 2) and 3) are solved by clearing mWindow, which signals to the
// pending event that we had shut down. In that case the event bails
// and does not touch mWindow.
//
// 4) happens through DisposeNative directly. DetachFromWindow is not
// called.
//
// 6) is solved by keeping a destroyed flag in the Java NPZC instance,
// and only make a pending call if the destroyed flag is not set.
//
// 7) is solved by taking a lock whenever mWindow is modified on the
// Gecko thread or accessed on the UI thread. That way, we don't
// release mWindow until the UI thread is done using it, thus avoiding
// the race condition.
typedef NativePanZoomController::GlobalRef NPZCRef;
auto callDestroy = [] (const NPZCRef& npzc) {
npzc->Destroy();
};
NativePanZoomController::GlobalRef npzc = mNPZC;
AndroidBridge::Bridge()->PostTaskToUiThread(NewRunnableFunction(
static_cast<void(*)(const NPZCRef&)>(callDestroy),
mozilla::Move(npzc)), 0);
// Signal to any pending calls on either Gecko or UI thread that NPZC
// is being destroyed.
MutexAutoLock lock(mWindowLock);
mWindow->mNPZCSupport = nullptr;
mWindow = nullptr;
}
private:
void DisposeOnGeckoThread()
{
MOZ_ASSERT(NS_IsMainThread());
if (mWindow && mWindow->mNPZCSupport == this) {
mWindow->mNPZCSupport = nullptr;
}
// Base::DisposeNative implicitly deletes 'this'.
Base::DisposeNative(NativePanZoomController::LocalRef(
jni::GetGeckoThreadEnv(), mNPZC));
}
public:
void DisposeNative()
{
// Capturing 'this' is okay because 'this' is owned by the Java
// instance, it is alive until this lambda is run, and we make sure to
// only call DisposeNative once.
nsAppShell::PostEvent([this] {
DisposeOnGeckoThread();
});
}
void AbortAnimation()
{
MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
MutexAutoLock lock(mWindowLock);
if (!mWindow) {
// We already shut down.
return;
}
RefPtr<APZCTreeManager> controller = mWindow->mAPZC;
RefPtr<CompositorParent> compositor = mWindow->mCompositorParent;
if (controller && compositor) {
// TODO: Pass in correct values for presShellId and viewId.
controller->CancelAnimation(ScrollableLayerGuid(
compositor->RootLayerTreeId(), 0, 0));
}
}
void SetIsLongpressEnabled(bool aIsLongpressEnabled)
{
MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
APZCTreeManager::SetLongTapEnabled(aIsLongpressEnabled);
}
bool HandleMotionEvent(const NativePanZoomController::LocalRef& aInstance,
int32_t aAction, int32_t aActionIndex,
int64_t aTime, int32_t aMetaState,
jni::IntArray::Param aPointerId,
jni::FloatArray::Param aX,
jni::FloatArray::Param aY,
jni::FloatArray::Param aOrientation,
jni::FloatArray::Param aPressure,
jni::FloatArray::Param aToolMajor,
jni::FloatArray::Param aToolMinor)
{
MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
MutexAutoLock lock(mWindowLock);
if (!mWindow) {
// We already shut down.
return false;
}
RefPtr<APZCTreeManager> controller = mWindow->mAPZC;
if (!controller) {
return false;
}
nsTArray<int32_t> pointerId(aPointerId);
MultiTouchInput::MultiTouchType type;
size_t startIndex = 0;
size_t endIndex = pointerId.Length();
switch (aAction) {
case AndroidMotionEvent::ACTION_DOWN:
case AndroidMotionEvent::ACTION_POINTER_DOWN:
type = MultiTouchInput::MULTITOUCH_START;
break;
case AndroidMotionEvent::ACTION_MOVE:
type = MultiTouchInput::MULTITOUCH_MOVE;
break;
case AndroidMotionEvent::ACTION_UP:
case AndroidMotionEvent::ACTION_POINTER_UP:
// for pointer-up events we only want the data from
// the one pointer that went up
type = MultiTouchInput::MULTITOUCH_END;
startIndex = aActionIndex;
endIndex = aActionIndex + 1;
break;
case AndroidMotionEvent::ACTION_OUTSIDE:
case AndroidMotionEvent::ACTION_CANCEL:
type = MultiTouchInput::MULTITOUCH_CANCEL;
break;
default:
return false;
}
MultiTouchInput input(type, aTime, TimeStamp(), 0);
input.modifiers = GetModifiers(aMetaState);
input.mTouches.SetCapacity(endIndex - startIndex);
nsTArray<float> x(aX);
nsTArray<float> y(aY);
nsTArray<float> orientation(aOrientation);
nsTArray<float> pressure(aPressure);
nsTArray<float> toolMajor(aToolMajor);
nsTArray<float> toolMinor(aToolMinor);
MOZ_ASSERT(pointerId.Length() == x.Length());
MOZ_ASSERT(pointerId.Length() == y.Length());
MOZ_ASSERT(pointerId.Length() == orientation.Length());
MOZ_ASSERT(pointerId.Length() == pressure.Length());
MOZ_ASSERT(pointerId.Length() == toolMajor.Length());
MOZ_ASSERT(pointerId.Length() == toolMinor.Length());
const nsIntPoint& offset =
mWindow->WidgetToScreenOffset().ToUnknownPoint();
for (size_t i = startIndex; i < endIndex; i++) {
float orien = orientation[i] * 180.0f / M_PI;
// w3c touchevents spec does not allow orientations == 90
// this shifts it to -90, which will be shifted to zero below
if (orien >= 90.0) {
orien -= 180.0f;
}
nsIntPoint point = nsIntPoint(int32_t(floorf(x[i])),
int32_t(floorf(y[i]))) - offset;
// w3c touchevent radii are given with an orientation between 0 and
// 90. The radii are found by removing the orientation and
// measuring the x and y radii of the resulting ellipse. For
// Android orientations >= 0 and < 90, use the y radius as the
// major radius, and x as the minor radius. However, for an
// orientation < 0, we have to shift the orientation by adding 90,
// and reverse which radius is major and minor.
gfx::Size radius;
if (orien < 0.0f) {
orien += 90.0f;
radius = gfx::Size(int32_t(toolMajor[i] / 2.0f),
int32_t(toolMinor[i] / 2.0f));
} else {
radius = gfx::Size(int32_t(toolMinor[i] / 2.0f),
int32_t(toolMajor[i] / 2.0f));
}
input.mTouches.AppendElement(SingleTouchData(
pointerId[i], ScreenIntPoint::FromUnknownPoint(point),
ScreenSize::FromUnknownSize(radius), orien, pressure[i]));
}
ScrollableLayerGuid guid;
uint64_t blockId;
nsEventStatus status =
controller->ReceiveInputEvent(input, &guid, &blockId);
if (status == nsEventStatus_eConsumeNoDefault) {
return true;
}
// Dispatch APZ input event on Gecko thread.
NativePanZoomController::GlobalRef npzc = mNPZC;
nsAppShell::PostEvent([npzc, input, guid, blockId, status] {
MOZ_ASSERT(NS_IsMainThread());
JNIEnv* const env = jni::GetGeckoThreadEnv();
NPZCSupport* npzcSupport = GetNative(
NativePanZoomController::LocalRef(env, npzc));
if (!npzcSupport || !npzcSupport->mWindow) {
// We already shut down.
env->ExceptionClear();
return;
}
nsWindow* const window = npzcSupport->mWindow;
window->UserActivity();
WidgetTouchEvent touchEvent = input.ToWidgetTouchEvent(window);
window->ProcessUntransformedAPZEvent(&touchEvent, guid,
blockId, status);
window->DispatchHitTest(touchEvent);
});
return true;
}
};
/**
* GLController has some unique requirements for its native calls, so make it
* separate from GeckoViewSupport.
@ -465,7 +737,7 @@ public:
, mGLController(aInstance)
, mCompositorPaused(true)
{
Reattach(aInstance);
Base::AttachNative(aInstance, this);
}
~GLControllerSupport()
@ -473,11 +745,6 @@ public:
mGLController->Destroy();
}
void Reattach(const GLController::LocalRef& aInstance)
{
Base::AttachNative(aInstance, this);
}
const GeckoLayerClient::Ref& GetLayerClient() const
{
return mLayerClient;
@ -711,6 +978,7 @@ nsWindow::InitNatives()
nsWindow::GeckoViewSupport::Base::Init();
nsWindow::GeckoViewSupport::EditableBase::Init();
nsWindow::GLControllerSupport::Init();
nsWindow::NPZCSupport::Init();
}
nsWindow*
@ -752,6 +1020,7 @@ nsWindow::DumpWindows(const nsTArray<nsWindow*>& wins, int indent)
}
nsWindow::nsWindow() :
mNPZCSupport(nullptr),
mIsVisible(false),
mParent(nullptr),
mAwaitingFullScreen(false),

View File

@ -57,6 +57,11 @@ private:
// Object that implements native GLController calls.
mozilla::UniquePtr<GLControllerSupport> mGLControllerSupport;
class NPZCSupport;
// Object that implements native NativePanZoomController calls.
// Owned by the Java NativePanZoomController instance.
NPZCSupport* mNPZCSupport;
public:
static void OnGlobalAndroidEvent(mozilla::AndroidGeckoEvent *ae);
static mozilla::gfx::IntSize GetAndroidScreenBounds();