Bug 1415074 - Fix unresponsiveness after restoring GeckoView states; r=me
Fix a bug where GeckoView becomes unresponsive to dispatched events
after restoring states, due to the native queue not being restored.
r=me for small, tested patch.
MozReview-Commit-ID: K1cVjjNaZK1
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java
@@ -66,16 +66,20 @@ public final class EventDispatcher exten
/* package */ EventDispatcher() {
mNativeQueue = GeckoThread.getNativeQueue();
}
/* package */ EventDispatcher(final NativeQueue queue) {
mNativeQueue = queue;
}
+ public NativeQueue getNativeQueue() {
+ return mNativeQueue;
+ }
+
private boolean isReadyForDispatchingToGecko() {
return mNativeQueue.isReady();
}
@WrapForJNI(dispatchTo = "gecko") @Override // JNIObject
protected native void disposeNative();
@WrapForJNI private static final int DETACHED = 0;
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSession.java
@@ -277,17 +277,17 @@ public class GeckoSession implements Par
mPermissionHandler.setListener(delegate, this);
}
private PromptDelegate mPromptDelegate;
private final Listener mListener = new Listener();
protected static final class Window extends JNIObject implements IInterface {
- private final NativeQueue mNativeQueue;
+ private NativeQueue mNativeQueue;
private Binder mBinder;
public Window(final NativeQueue nativeQueue) {
mNativeQueue = nativeQueue;
}
@Override // IInterface
public IBinder asBinder() {
@@ -305,16 +305,28 @@ public class GeckoSession implements Par
@WrapForJNI(dispatchTo = "proxy")
@Override protected native void disposeNative();
@WrapForJNI(dispatchTo = "proxy")
native void close();
@WrapForJNI(dispatchTo = "proxy")
+ native void transfer(EventDispatcher dispatcher, GeckoBundle settings);
+
+ @WrapForJNI(calledFrom = "gecko")
+ private synchronized void onTransfer(final EventDispatcher dispatcher) {
+ final NativeQueue nativeQueue = dispatcher.getNativeQueue();
+ if (mNativeQueue != nativeQueue) {
+ nativeQueue.setState(mNativeQueue.getState());
+ mNativeQueue = nativeQueue;
+ }
+ }
+
+ @WrapForJNI(dispatchTo = "proxy")
native void attach(GeckoView view, Object compositor);
@WrapForJNI(calledFrom = "gecko")
private synchronized void onReady() {
if (mNativeQueue.checkAndSetState(State.INITIAL, State.READY)) {
Log.i(LOGTAG, "zerdatime " + SystemClock.elapsedRealtime() +
" - chrome startup finished");
}
@@ -353,55 +365,61 @@ public class GeckoSession implements Par
mSettings = new GeckoSessionSettings(this);
} else {
mSettings = new GeckoSessionSettings(settings, this);
}
mListener.registerListeners();
}
- /* package */ void transferFrom(final GeckoSession session) {
+ private void transferFrom(final Window window, final GeckoSessionSettings settings) {
if (isOpen()) {
throw new IllegalStateException("Session is open");
}
- mWindow = session.mWindow;
- mSettings = new GeckoSessionSettings(session.mSettings, this);
+ mWindow = window;
+ mSettings = new GeckoSessionSettings(settings, this);
+
+ if (mWindow != null) {
+ if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
+ mWindow.transfer(mEventDispatcher, mSettings.asBundle());
+ } else {
+ GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
+ mWindow, "transfer",
+ EventDispatcher.class, mEventDispatcher,
+ GeckoBundle.class, mSettings.asBundle());
+ }
+ }
+ }
+
+ /* package */ void transferFrom(final GeckoSession session) {
+ transferFrom(session.mWindow, session.mSettings);
session.mWindow = null;
}
@Override // Parcelable
public int describeContents() {
return 0;
}
@Override // Parcelable
public void writeToParcel(Parcel out, int flags) {
out.writeStrongInterface(mWindow);
out.writeParcelable(mSettings, flags);
}
// AIDL code may call readFromParcel even though it's not part of Parcelable.
public void readFromParcel(final Parcel source) {
- if (isOpen()) {
- throw new IllegalStateException("Session is open");
- }
-
final IBinder binder = source.readStrongBinder();
- final IInterface window = (binder != null) ?
+ final IInterface ifce = (binder != null) ?
binder.queryLocalInterface(Window.class.getName()) : null;
- if (window instanceof Window) {
- mWindow = (Window) window;
- } else {
- mWindow = null;
- }
-
+ final Window window = (ifce instanceof Window) ? (Window) ifce : null;
final GeckoSessionSettings settings =
source.readParcelable(getClass().getClassLoader());
- mSettings = new GeckoSessionSettings(settings, this);
+ transferFrom(window, settings);
}
public static final Creator<GeckoSession> CREATOR = new Creator<GeckoSession>() {
@Override
public GeckoSession createFromParcel(final Parcel in) {
final GeckoSession session = new GeckoSession();
session.readFromParcel(in);
return session;
--- a/widget/android/GeneratedJNINatives.h
+++ b/widget/android/GeneratedJNINatives.h
@@ -217,17 +217,17 @@ const JNINativeMethod GeckoScreenOrienta
mozilla::jni::NativeStub<GeckoScreenOrientation::OnOrientationChange_t, Impl>
::template Wrap<&Impl::OnOrientationChange>)
};
template<class Impl>
class GeckoSession::Window::Natives : public mozilla::jni::NativeImpl<Window, Impl>
{
public:
- static const JNINativeMethod methods[4];
+ static const JNINativeMethod methods[5];
};
template<class Impl>
const JNINativeMethod GeckoSession::Window::Natives<Impl>::methods[] = {
mozilla::jni::MakeNativeMethod<GeckoSession::Window::Attach_t>(
mozilla::jni::NativeStub<GeckoSession::Window::Attach_t, Impl>
::template Wrap<&Impl::Attach>),
@@ -237,17 +237,21 @@ const JNINativeMethod GeckoSession::Wind
::template Wrap<&Impl::Close>),
mozilla::jni::MakeNativeMethod<GeckoSession::Window::DisposeNative_t>(
mozilla::jni::NativeStub<GeckoSession::Window::DisposeNative_t, Impl>
::template Wrap<&Impl::DisposeNative>),
mozilla::jni::MakeNativeMethod<GeckoSession::Window::Open_t>(
mozilla::jni::NativeStub<GeckoSession::Window::Open_t, Impl>
- ::template Wrap<&Impl::Open>)
+ ::template Wrap<&Impl::Open>),
+
+ mozilla::jni::MakeNativeMethod<GeckoSession::Window::Transfer_t>(
+ mozilla::jni::NativeStub<GeckoSession::Window::Transfer_t, Impl>
+ ::template Wrap<&Impl::Transfer>)
};
template<class Impl>
class GeckoThread::Natives : public mozilla::jni::NativeImpl<GeckoThread, Impl>
{
public:
static const JNINativeMethod methods[7];
};
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -787,19 +787,30 @@ constexpr char GeckoSession::Window::Dis
constexpr char GeckoSession::Window::OnReady_t::name[];
constexpr char GeckoSession::Window::OnReady_t::signature[];
auto GeckoSession::Window::OnReady() const -> void
{
return mozilla::jni::Method<OnReady_t>::Call(Window::mCtx, nullptr);
}
+constexpr char GeckoSession::Window::OnTransfer_t::name[];
+constexpr char GeckoSession::Window::OnTransfer_t::signature[];
+
+auto GeckoSession::Window::OnTransfer(mozilla::jni::Object::Param a0) const -> void
+{
+ return mozilla::jni::Method<OnTransfer_t>::Call(Window::mCtx, nullptr, a0);
+}
+
constexpr char GeckoSession::Window::Open_t::name[];
constexpr char GeckoSession::Window::Open_t::signature[];
+constexpr char GeckoSession::Window::Transfer_t::name[];
+constexpr char GeckoSession::Window::Transfer_t::signature[];
+
const char GeckoThread::name[] =
"org/mozilla/gecko/GeckoThread";
constexpr char GeckoThread::CheckAndSetState_t::name[];
constexpr char GeckoThread::CheckAndSetState_t::signature[];
auto GeckoThread::CheckAndSetState(mozilla::jni::Object::Param a0, mozilla::jni::Object::Param a1) -> bool
{
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -2415,16 +2415,36 @@ public:
static const mozilla::jni::CallingThread callingThread =
mozilla::jni::CallingThread::GECKO;
static const mozilla::jni::DispatchTarget dispatchTarget =
mozilla::jni::DispatchTarget::CURRENT;
};
auto OnReady() const -> void;
+ struct OnTransfer_t {
+ typedef Window Owner;
+ typedef void ReturnType;
+ typedef void SetterType;
+ typedef mozilla::jni::Args<
+ mozilla::jni::Object::Param> Args;
+ static constexpr char name[] = "onTransfer";
+ static constexpr char signature[] =
+ "(Lorg/mozilla/gecko/EventDispatcher;)V";
+ static const bool isStatic = false;
+ static const mozilla::jni::ExceptionMode exceptionMode =
+ mozilla::jni::ExceptionMode::ABORT;
+ static const mozilla::jni::CallingThread callingThread =
+ mozilla::jni::CallingThread::GECKO;
+ static const mozilla::jni::DispatchTarget dispatchTarget =
+ mozilla::jni::DispatchTarget::CURRENT;
+ };
+
+ auto OnTransfer(mozilla::jni::Object::Param) const -> void;
+
struct Open_t {
typedef Window Owner;
typedef void ReturnType;
typedef void SetterType;
typedef mozilla::jni::Args<
Window::Param,
mozilla::jni::Object::Param,
mozilla::jni::Object::Param,
@@ -2438,16 +2458,35 @@ public:
static const mozilla::jni::ExceptionMode exceptionMode =
mozilla::jni::ExceptionMode::ABORT;
static const mozilla::jni::CallingThread callingThread =
mozilla::jni::CallingThread::ANY;
static const mozilla::jni::DispatchTarget dispatchTarget =
mozilla::jni::DispatchTarget::PROXY;
};
+ struct Transfer_t {
+ typedef Window Owner;
+ typedef void ReturnType;
+ typedef void SetterType;
+ typedef mozilla::jni::Args<
+ mozilla::jni::Object::Param,
+ mozilla::jni::Object::Param> Args;
+ static constexpr char name[] = "transfer";
+ static constexpr char signature[] =
+ "(Lorg/mozilla/gecko/EventDispatcher;Lorg/mozilla/gecko/util/GeckoBundle;)V";
+ static const bool isStatic = false;
+ static const mozilla::jni::ExceptionMode exceptionMode =
+ mozilla::jni::ExceptionMode::ABORT;
+ static const mozilla::jni::CallingThread callingThread =
+ mozilla::jni::CallingThread::ANY;
+ static const mozilla::jni::DispatchTarget dispatchTarget =
+ mozilla::jni::DispatchTarget::PROXY;
+ };
+
static const mozilla::jni::CallingThread callingThread =
mozilla::jni::CallingThread::ANY;
template<class Impl> class Natives;
};
class GeckoThread : public mozilla::jni::ObjectBase<GeckoThread>
{
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -283,16 +283,21 @@ public:
jni::Object::Param aSettings,
jni::String::Param aChromeURI,
int32_t aScreenId,
bool aPrivateMode);
// Close and destroy the nsWindow.
void Close();
+ // Transfer this nsWindow to new GeckoSession objects.
+ void Transfer(const GeckoSession::Window::LocalRef& inst,
+ jni::Object::Param aDispatcher,
+ jni::Object::Param aSettings);
+
// Reattach this nsWindow to a new GeckoView.
void Attach(const GeckoSession::Window::LocalRef& inst,
jni::Object::Param aView, jni::Object::Param aCompositor);
void EnableEventDispatcher();
};
/**
@@ -1349,16 +1354,32 @@ nsWindow::GeckoViewSupport::Close()
}
mDOMWindow->ForceClose();
mDOMWindow = nullptr;
mGeckoViewWindow = nullptr;
}
void
+nsWindow::GeckoViewSupport::Transfer(const GeckoSession::Window::LocalRef& inst,
+ jni::Object::Param aDispatcher,
+ jni::Object::Param aSettings)
+{
+ if (!window.mAndroidView) {
+ return;
+ }
+
+ window.mAndroidView->mEventDispatcher->Attach(
+ java::EventDispatcher::Ref::From(aDispatcher), mDOMWindow);
+ window.mAndroidView->mSettings = java::GeckoBundle::Ref::From(aSettings);
+
+ inst->OnTransfer(aDispatcher);
+}
+
+void
nsWindow::GeckoViewSupport::Attach(const GeckoSession::Window::LocalRef& inst,
jni::Object::Param aView,
jni::Object::Param aCompositor)
{
// Associate our previous GeckoEditable with the new GeckoView.
MOZ_ASSERT(window.mEditable);
window.mEditable->OnViewChange(aView);