Bug 1415074 - Fix unresponsiveness after restoring GeckoView states; r=jchen
authorJim Chen <nchen@mozilla.com>
Tue, 07 Nov 2017 01:53:11 -0500
changeset 443737 597025d8888fa91b9418231f33e65424d384d83f
parent 443736 b1216f1952e290ca40982e83dd29128dbe79ce88
child 443738 80fbacdf4ca9488f72dad1bd6883679fb23e7288
child 443759 923836aebbc328d1a971f6ce32a99d9aa4d1345a
push id1618
push userCallek@gmail.com
push dateThu, 11 Jan 2018 17:45:48 +0000
treeherdermozilla-release@882ca853e05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjchen
bugs1415074
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1415074 - Fix unresponsiveness after restoring GeckoView states; r=jchen 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);