Bug 1442250 - 5. Reset native queue early when transferring; r?esawin draft
authorJim Chen <nchen@mozilla.com>
Fri, 09 Mar 2018 12:34:38 -0500
changeset 765371 92e33142338d77166aac72a28de7f94d4801469a
parent 765370 0d67b4bd6047c7fc31258f421c09c233a9ddd42c
push id102052
push userbmo:nchen@mozilla.com
push dateFri, 09 Mar 2018 17:35:14 +0000
reviewersesawin
bugs1442250
milestone60.0a1
Bug 1442250 - 5. Reset native queue early when transferring; r?esawin When we reset the old native queue when transferring to another session, perform the reset right after the transfer() call, instead of in onTransfer(), which is too late for clearing stale pending calls. Then, after transferring to a new queue, let Gecko call Window.onReady to set the new queue's state if needed. That way the Java queue state is consistent with the Gecko state. MozReview-Commit-ID: CUXGrhR4FCD
mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
widget/android/GeneratedJNINatives.h
widget/android/GeneratedJNIWrappers.cpp
widget/android/GeneratedJNIWrappers.h
widget/android/nsAppShell.cpp
widget/android/nsWindow.cpp
widget/android/nsWindow.h
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java
@@ -67,20 +67,16 @@ public final class EventDispatcher exten
     /* package */ EventDispatcher() {
         mNativeQueue = GeckoThread.getNativeQueue();
     }
 
     public 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/geckoview/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
@@ -407,35 +407,37 @@ public class GeckoSession extends LayerS
         public Binder asBinder() {
             if (mBinder == null) {
                 mBinder = new Binder();
                 mBinder.attachInterface(this, Window.class.getName());
             }
             return mBinder;
         }
 
+        // Create a new Gecko window and assign an initial set of Java session objects to it.
         @WrapForJNI(dispatchTo = "proxy")
-        public static native void open(Window instance, Compositor compositor,
-                                       EventDispatcher dispatcher,
-                                       GeckoBundle settings, String chromeUri,
-                                       int screenId, boolean privateMode, String id);
+        public static native void open(Window instance, NativeQueue queue,
+                                       Compositor compositor, EventDispatcher dispatcher,
+                                       GeckoBundle settings, String id, String chromeUri,
+                                       int screenId, boolean privateMode);
 
         @Override // JNIObject
         public void disposeNative() {
             if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
                 nativeDisposeNative();
             } else {
                 GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
                         this, "nativeDisposeNative");
             }
         }
 
         @WrapForJNI(dispatchTo = "proxy", stubName = "DisposeNative")
         private native void nativeDisposeNative();
 
+        // Force the underlying Gecko window to close and release assigned Java objects.
         public void close() {
             // Reset our queue, so we don't end up with queued calls on a disposed object.
             synchronized (this) {
                 if (mNativeQueue == null) {
                     // Already closed elsewhere.
                     return;
                 }
                 mNativeQueue.reset(State.INITIAL);
@@ -452,41 +454,71 @@ public class GeckoSession extends LayerS
                 GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
                         this, "nativeClose");
             }
         }
 
         @WrapForJNI(dispatchTo = "proxy", stubName = "Close")
         private native void nativeClose();
 
-        @WrapForJNI(dispatchTo = "proxy")
-        public native void transfer(Compositor compositor, EventDispatcher dispatcher,
-                                    GeckoBundle settings);
+        // Assign a new set of Java session objects to the underlying Gecko window.
+        // This replaces previously assigned objects from open() or transfer() calls.
+        public synchronized void transfer(final NativeQueue queue,
+                                          final Compositor compositor,
+                                          final EventDispatcher dispatcher,
+                                          final GeckoBundle settings) {
+            if (mNativeQueue == null) {
+                // Already closed.
+                return;
+            }
 
-        @WrapForJNI(calledFrom = "gecko")
-        private synchronized void onTransfer(final EventDispatcher dispatcher) {
-            final NativeQueue nativeQueue = dispatcher.getNativeQueue();
-            if (mNativeQueue != null && mNativeQueue != nativeQueue) {
-                // Set new queue to the same state as the old queue,
-                // then return the old queue to its initial state if applicable,
-                // because the old queue is no longer the active queue.
-                nativeQueue.setState(mNativeQueue.getState());
+            if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
+                nativeTransfer(queue, compositor, dispatcher, settings);
+            } else {
+                GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
+                        this, "nativeTransfer",
+                        NativeQueue.class, queue,
+                        Compositor.class, compositor,
+                        EventDispatcher.class, dispatcher,
+                        GeckoBundle.class, settings);
+            }
+
+            if (mNativeQueue != queue) {
+                // Reset the old queue to prevent old events from affecting this window.
+                // Gecko will call onReady later with the new queue if needed.
                 mNativeQueue.reset(State.INITIAL);
-                mNativeQueue = nativeQueue;
+                mNativeQueue = queue;
             }
         }
 
+        @WrapForJNI(dispatchTo = "proxy", stubName = "Transfer")
+        private native void nativeTransfer(NativeQueue queue, Compositor compositor,
+                                           EventDispatcher dispatcher, GeckoBundle settings);
+
         @WrapForJNI(dispatchTo = "proxy")
         public native void attachEditable(IGeckoEditableParent parent,
                                           GeckoEditableChild child);
 
         @WrapForJNI(calledFrom = "gecko")
-        private synchronized void onReady() {
-            if (mNativeQueue != null &&
-                    mNativeQueue.checkAndSetState(State.INITIAL, State.READY)) {
+        private synchronized void onReady(final @Nullable NativeQueue queue) {
+            // onReady is called the first time the Gecko window is ready, with a null queue
+            // argument. In this case, we simply set the current queue to ready state.
+            //
+            // After the initial call, onReady is called again every time Window.transfer()
+            // is called, with a non-null queue argument. In this case, we only set the
+            // current queue to ready state _if_ the current queue matches the given queue,
+            // because if the queues don't match, we know there is another onReady call coming.
+
+            if ((queue == null && mNativeQueue == null) ||
+                (queue != null && mNativeQueue != queue)) {
+                return;
+            }
+
+            if (mNativeQueue.checkAndSetState(State.INITIAL, State.READY) &&
+                    queue == null) {
                 Log.i(LOGTAG, "zerdatime " + SystemClock.elapsedRealtime() +
                       " - chrome startup finished");
             }
         }
     }
 
     private class Listener implements BundleEventListener {
         /* package */ void registerListeners() {
@@ -534,25 +566,18 @@ public class GeckoSession extends LayerS
             onWindowChanged(WINDOW_TRANSFER_IN, /* inProgress */ true);
         }
 
         mWindow = window;
         mSettings = new GeckoSessionSettings(settings, this);
         mId = id;
 
         if (mWindow != null) {
-            if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
-                mWindow.transfer(mCompositor, mEventDispatcher, mSettings.asBundle());
-            } else {
-                GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
-                        mWindow, "transfer",
-                        Compositor.class, mCompositor,
-                        EventDispatcher.class, mEventDispatcher,
-                        GeckoBundle.class, mSettings.asBundle());
-            }
+            mWindow.transfer(mNativeQueue, mCompositor,
+                             mEventDispatcher, mSettings.asBundle());
 
             onWindowChanged(WINDOW_TRANSFER_IN, /* inProgress */ false);
         }
     }
 
     /* package */ void transferFrom(final GeckoSession session) {
         final boolean changing = (session.mWindow != null);
         if (changing) {
@@ -684,28 +709,30 @@ public class GeckoSession extends LayerS
         final int screenId = mSettings.getInt(GeckoSessionSettings.SCREEN_ID);
         final boolean isPrivate = mSettings.getBoolean(GeckoSessionSettings.USE_PRIVATE_MODE);
 
         mWindow = new Window(mNativeQueue);
 
         onWindowChanged(WINDOW_OPEN, /* inProgress */ true);
 
         if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
-            Window.open(mWindow, mCompositor, mEventDispatcher,
-                        mSettings.asBundle(), chromeUri, screenId, isPrivate, mId);
+            Window.open(mWindow, mNativeQueue, mCompositor, mEventDispatcher,
+                        mSettings.asBundle(), mId, chromeUri, screenId, isPrivate);
         } else {
             GeckoThread.queueNativeCallUntil(
                 GeckoThread.State.PROFILE_READY,
                 Window.class, "open",
                 Window.class, mWindow,
+                NativeQueue.class, mNativeQueue,
                 Compositor.class, mCompositor,
                 EventDispatcher.class, mEventDispatcher,
                 GeckoBundle.class, mSettings.asBundle(),
+                String.class, mId,
                 String.class, chromeUri,
-                screenId, isPrivate, mId);
+                screenId, isPrivate);
         }
 
         onWindowChanged(WINDOW_OPEN, /* inProgress */ false);
     }
 
     /**
      * Closes the session.
      *
--- a/widget/android/GeneratedJNINatives.h
+++ b/widget/android/GeneratedJNINatives.h
@@ -590,20 +590,20 @@ const JNINativeMethod GeckoSession::Wind
     mozilla::jni::MakeNativeMethod<GeckoSession::Window::Close_t>(
             mozilla::jni::NativeStub<GeckoSession::Window::Close_t, Impl>
             ::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::Transfer_t>(
+            mozilla::jni::NativeStub<GeckoSession::Window::Transfer_t, Impl>
+            ::template Wrap<&Impl::Transfer>),
+
     mozilla::jni::MakeNativeMethod<GeckoSession::Window::Open_t>(
             mozilla::jni::NativeStub<GeckoSession::Window::Open_t, Impl>
-            ::template Wrap<&Impl::Open>),
-
-    mozilla::jni::MakeNativeMethod<GeckoSession::Window::Transfer_t>(
-            mozilla::jni::NativeStub<GeckoSession::Window::Transfer_t, Impl>
-            ::template Wrap<&Impl::Transfer>)
+            ::template Wrap<&Impl::Open>)
 };
 
 } /* java */
 } /* mozilla */
 #endif // GeneratedJNINatives_h
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -2518,38 +2518,30 @@ constexpr char GeckoSession::Window::Att
 constexpr char GeckoSession::Window::AttachEditable_t::signature[];
 
 constexpr char GeckoSession::Window::Close_t::name[];
 constexpr char GeckoSession::Window::Close_t::signature[];
 
 constexpr char GeckoSession::Window::DisposeNative_t::name[];
 constexpr char GeckoSession::Window::DisposeNative_t::signature[];
 
+constexpr char GeckoSession::Window::Transfer_t::name[];
+constexpr char GeckoSession::Window::Transfer_t::signature[];
+
 constexpr char GeckoSession::Window::OnReady_t::name[];
 constexpr char GeckoSession::Window::OnReady_t::signature[];
 
-auto GeckoSession::Window::OnReady() const -> void
+auto GeckoSession::Window::OnReady(mozilla::jni::Object::Param a0) 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);
+    return mozilla::jni::Method<OnReady_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 TextInputController::name[] =
         "org/mozilla/geckoview/TextInputController";
 
 const char TextInputController::EditableClient::name[] =
         "org/mozilla/geckoview/TextInputController$EditableClient";
 
 const char TextInputController::EditableListener::name[] =
         "org/mozilla/geckoview/TextInputController$EditableListener";
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -7173,92 +7173,75 @@ 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,
+                mozilla::jni::Object::Param,
+                mozilla::jni::Object::Param> Args;
+        static constexpr char name[] = "nativeTransfer";
+        static constexpr char signature[] =
+                "(Lorg/mozilla/gecko/NativeQueue;Lorg/mozilla/gecko/gfx/LayerSession$Compositor;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;
+    };
+
     struct OnReady_t {
         typedef Window Owner;
         typedef void ReturnType;
         typedef void SetterType;
-        typedef mozilla::jni::Args<> Args;
+        typedef mozilla::jni::Args<
+                mozilla::jni::Object::Param> Args;
         static constexpr char name[] = "onReady";
         static constexpr char signature[] =
-                "()V";
+                "(Lorg/mozilla/gecko/NativeQueue;)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 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;
+    auto OnReady(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,
                 mozilla::jni::Object::Param,
+                mozilla::jni::Object::Param,
+                mozilla::jni::String::Param,
                 mozilla::jni::String::Param,
                 int32_t,
-                bool,
-                mozilla::jni::String::Param> Args;
+                bool> Args;
         static constexpr char name[] = "open";
         static constexpr char signature[] =
-                "(Lorg/mozilla/geckoview/GeckoSession$Window;Lorg/mozilla/gecko/gfx/LayerSession$Compositor;Lorg/mozilla/gecko/EventDispatcher;Lorg/mozilla/gecko/util/GeckoBundle;Ljava/lang/String;IZLjava/lang/String;)V";
-        static const bool isStatic = true;
-        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,
-                mozilla::jni::Object::Param> Args;
-        static constexpr char name[] = "transfer";
-        static constexpr char signature[] =
-                "(Lorg/mozilla/gecko/gfx/LayerSession$Compositor;Lorg/mozilla/gecko/EventDispatcher;Lorg/mozilla/gecko/util/GeckoBundle;)V";
-        static const bool isStatic = false;
+                "(Lorg/mozilla/geckoview/GeckoSession$Window;Lorg/mozilla/gecko/NativeQueue;Lorg/mozilla/gecko/gfx/LayerSession$Compositor;Lorg/mozilla/gecko/EventDispatcher;Lorg/mozilla/gecko/util/GeckoBundle;Ljava/lang/String;Ljava/lang/String;IZ)V";
+        static const bool isStatic = true;
         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;
     };
 
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -617,17 +617,17 @@ nsAppShell::Observe(nsISupports* aSubjec
             if (jni::IsAvailable()) {
                 // When our first window has loaded, assume any JS
                 // initialization has run and set Gecko to ready.
                 java::GeckoThread::CheckAndSetState(
                         java::GeckoThread::State::PROFILE_READY(),
                         java::GeckoThread::State::RUNNING());
             }
             const auto window = static_cast<nsWindow*>(widget.get());
-            window->EnableEventDispatcher();
+            window->OnGeckoViewReady();
         }
     } else if (!strcmp(aTopic, "quit-application")) {
         if (jni::IsAvailable()) {
             const bool restarting =
                     aData && NS_LITERAL_STRING("restart").Equals(aData);
             java::GeckoThread::SetState(
                     restarting ?
                     java::GeckoThread::State::RESTARTING() :
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -267,43 +267,46 @@ public:
 
     using Base::DisposeNative;
 
     /**
      * GeckoView methods
      */
 private:
     nsCOMPtr<nsPIDOMWindowOuter> mDOMWindow;
+    bool mIsReady{false};
 
 public:
     // Create and attach a window.
     static void Open(const jni::Class::LocalRef& aCls,
                      GeckoSession::Window::Param aWindow,
+                     jni::Object::Param aQueue,
                      jni::Object::Param aCompositor,
                      jni::Object::Param aDispatcher,
                      jni::Object::Param aSettings,
+                     jni::String::Param aId,
                      jni::String::Param aChromeURI,
                      int32_t aScreenId,
-                     bool aPrivateMode,
-                     jni::String::Param aId);
+                     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 aQueue,
                   jni::Object::Param aCompositor,
                   jni::Object::Param aDispatcher,
                   jni::Object::Param aSettings);
 
     void AttachEditable(const GeckoSession::Window::LocalRef& inst,
                         jni::Object::Param aEditableParent,
                         jni::Object::Param aEditableChild);
 
-    void EnableEventDispatcher();
+    void OnReady(jni::Object::Param aQueue = nullptr);
 };
 
 /**
  * NativePanZoomController handles its native calls on the UI thread, so make
  * it separate from GeckoViewSupport.
  */
 class nsWindow::NPZCSupport final
     : public NativePanZoomController::Natives<NPZCSupport>
@@ -1151,23 +1154,24 @@ nsWindow::GeckoViewSupport::~GeckoViewSu
     if (window.mLayerViewSupport) {
         window.mLayerViewSupport.Detach();
     }
 }
 
 /* static */ void
 nsWindow::GeckoViewSupport::Open(const jni::Class::LocalRef& aCls,
                                  GeckoSession::Window::Param aWindow,
+                                 jni::Object::Param aQueue,
                                  jni::Object::Param aCompositor,
                                  jni::Object::Param aDispatcher,
                                  jni::Object::Param aSettings,
+                                 jni::String::Param aId,
                                  jni::String::Param aChromeURI,
                                  int32_t aScreenId,
-                                 bool aPrivateMode,
-                                 jni::String::Param aId)
+                                 bool aPrivateMode)
 {
     MOZ_ASSERT(NS_IsMainThread());
 
     AUTO_PROFILER_LABEL("nsWindow::GeckoViewSupport::Open", OTHER);
 
     nsCOMPtr<nsIWindowWatcher> ww = do_GetService(NS_WINDOWWATCHER_CONTRACTID);
     MOZ_RELEASE_ASSERT(ww);
 
@@ -1208,17 +1212,17 @@ nsWindow::GeckoViewSupport::Open(const j
     GeckoSession::Window::LocalRef sessionWindow(aCls.Env(), aWindow);
     window->mGeckoViewSupport =
             mozilla::MakeUnique<GeckoViewSupport>(window, sessionWindow);
     window->mGeckoViewSupport->mDOMWindow = pdomWindow;
     window->mAndroidView = androidView;
 
     // Attach other session support objects.
     window->mGeckoViewSupport->Transfer(
-            sessionWindow, aCompositor, aDispatcher, aSettings);
+            sessionWindow, aQueue, aCompositor, aDispatcher, aSettings);
 
     if (window->mWidgetListener) {
         nsCOMPtr<nsIXULWindow> xulWindow(
                 window->mWidgetListener->GetXULWindow());
         if (xulWindow) {
             // Our window is not intrinsically sized, so tell nsXULWindow to
             // not set a size for us.
             xulWindow->SetIntrinsicallySized(false);
@@ -1239,16 +1243,17 @@ nsWindow::GeckoViewSupport::Close()
 
     mDOMWindow->ForceClose();
     mDOMWindow = nullptr;
     mGeckoViewWindow = nullptr;
 }
 
 void
 nsWindow::GeckoViewSupport::Transfer(const GeckoSession::Window::LocalRef& inst,
+                                     jni::Object::Param aQueue,
                                      jni::Object::Param aCompositor,
                                      jni::Object::Param aDispatcher,
                                      jni::Object::Param aSettings)
 {
     if (window.mNPZCSupport) {
         MOZ_ASSERT(window.mLayerViewSupport);
         window.mNPZCSupport.Detach();
     }
@@ -1261,17 +1266,19 @@ nsWindow::GeckoViewSupport::Transfer(con
             inst.Env(), LayerSession::Compositor::Ref::From(aCompositor));
     window.mLayerViewSupport.Attach(compositor, &window, compositor);
 
     MOZ_ASSERT(window.mAndroidView);
     window.mAndroidView->mEventDispatcher->Attach(
             java::EventDispatcher::Ref::From(aDispatcher), mDOMWindow);
     window.mAndroidView->mSettings = java::GeckoBundle::Ref::From(aSettings);
 
-    inst->OnTransfer(aDispatcher);
+    if (mIsReady) {
+        OnReady(aQueue);
+    }
 
     DispatchToUiThread(
             "GeckoViewSupport::Transfer",
             [compositor = LayerSession::Compositor::GlobalRef(compositor)] {
                 compositor->OnCompositorAttached();
             });
 
     // Set the first-paint flag so that we refresh viewports, etc.
@@ -1482,22 +1489,22 @@ nsWindow::GetUiCompositorControllerChild
 
 int64_t
 nsWindow::GetRootLayerId() const
 {
     return mCompositorSession ? mCompositorSession->RootLayerTreeId() : 0;
 }
 
 void
-nsWindow::EnableEventDispatcher()
+nsWindow::OnGeckoViewReady()
 {
     if (!mGeckoViewSupport) {
         return;
     }
-    mGeckoViewSupport->EnableEventDispatcher();
+    mGeckoViewSupport->OnReady();
 }
 
 void
 nsWindow::SetParent(nsIWidget *aNewParent)
 {
     if ((nsIWidget*)mParent == aNewParent)
         return;
 
@@ -2040,22 +2047,23 @@ nsWindow::GetEventTimeStamp(int64_t aEve
     // Our posix implemententaion of TimeStamp::Now uses SYSTEM_TIME_MONOTONIC
     //  too. Due to same implementation, we can use this via FromSystemTime.
     int64_t tick =
         BaseTimeDurationPlatformUtils::TicksFromMilliseconds(aEventTime);
     return TimeStamp::FromSystemTime(tick);
 }
 
 void
-nsWindow::GeckoViewSupport::EnableEventDispatcher()
+nsWindow::GeckoViewSupport::OnReady(jni::Object::Param aQueue)
 {
     if (!mGeckoViewWindow) {
         return;
     }
-    mGeckoViewWindow->OnReady();
+    mGeckoViewWindow->OnReady(aQueue);
+    mIsReady = true;
 }
 
 void
 nsWindow::UserActivity()
 {
   if (!mIdleService) {
     mIdleService = do_GetService("@mozilla.org/widget/idleservice;1");
   }
--- a/widget/android/nsWindow.h
+++ b/widget/android/nsWindow.h
@@ -49,17 +49,17 @@ public:
     using nsBaseWidget::GetLayerManager;
 
     nsWindow();
 
     NS_INLINE_DECL_REFCOUNTING_INHERITED(nsWindow, nsBaseWidget)
 
     static void InitNatives();
     void SetScreenId(uint32_t aScreenId) { mScreenId = aScreenId; }
-    void EnableEventDispatcher();
+    void OnGeckoViewReady();
 
 private:
     uint32_t mScreenId;
 
     // An Event subclass that guards against stale events.
     template<typename Lambda,
              bool IsStatic = Lambda::isStatic,
              typename InstanceType = typename Lambda::ThisArgType,