Bug 1415074 - Fix unresponsiveness after restoring GeckoView states; r=me draft
authorJim Chen <nchen@mozilla.com>
Tue, 07 Nov 2017 01:53:11 -0500
changeset 693945 f9509b3c99bc15e777adf4aff16a4d2286235d96
parent 693762 7aa5ae735dc7025f1aade7b713eb5f643fea22c3
child 739213 c2f63d69dc11b38f56a272c56c14093061bc0b6f
push id87997
push userbmo:nchen@mozilla.com
push dateTue, 07 Nov 2017 06:53:24 +0000
reviewersme
bugs1415074
milestone58.0a1
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
mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSession.java
widget/android/GeneratedJNINatives.h
widget/android/GeneratedJNIWrappers.cpp
widget/android/GeneratedJNIWrappers.h
widget/android/nsWindow.cpp
--- 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);