Bug 1458327 - 1. Pass init-data instead of settings to Window; r?esawin draft
authorJim Chen <nchen@mozilla.com>
Fri, 04 May 2018 21:08:10 -0400
changeset 791728 fd995e8be81dd439bfb1b5f44666443012665657
parent 791710 5207b1392b11db534550a5eb801302e6dbb58f95
child 791729 15f1640439d9f66b4dece01bb32ae7290e459301
push id108891
push userbmo:nchen@mozilla.com
push dateSat, 05 May 2018 01:08:49 +0000
reviewersesawin
bugs1458327
milestone61.0a1
Bug 1458327 - 1. Pass init-data instead of settings to Window; r?esawin Instead of passing a live settings object to the native Window, pass a static initialization data bundle to the Window. The bundle contains settings at the time of creation. All changes to settings after creation are updated through events, rather than the live object. Using a live object between Gecko and UI threads has some drawbacks, including the need to lock the object, and the fact it won't work with remote runtimes across processes. MozReview-Commit-ID: 1DngLfJ0Fnc
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionSettings.java
widget/android/EventDispatcher.cpp
widget/android/nsIAndroidBridge.idl
widget/android/nsWindow.cpp
widget/android/nsWindow.h
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
@@ -498,17 +498,17 @@ public class GeckoSession extends LayerS
             }
             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, NativeQueue queue,
                                        Compositor compositor, EventDispatcher dispatcher,
-                                       GeckoBundle settings, String id, String chromeUri,
+                                       GeckoBundle initData, 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,
@@ -546,44 +546,44 @@ public class GeckoSession extends LayerS
         @WrapForJNI(dispatchTo = "proxy", stubName = "Close")
         private native void nativeClose();
 
         // 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) {
+                                          final GeckoBundle initData) {
             if (mNativeQueue == null) {
                 // Already closed.
                 return;
             }
 
             if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
-                nativeTransfer(queue, compositor, dispatcher, settings);
+                nativeTransfer(queue, compositor, dispatcher, initData);
             } else {
                 GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
                         this, "nativeTransfer",
                         NativeQueue.class, queue,
                         Compositor.class, compositor,
                         EventDispatcher.class, dispatcher,
-                        GeckoBundle.class, settings);
+                        GeckoBundle.class, initData);
             }
 
             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 = queue;
             }
         }
 
         @WrapForJNI(dispatchTo = "proxy", stubName = "Transfer")
         private native void nativeTransfer(NativeQueue queue, Compositor compositor,
-                                           EventDispatcher dispatcher, GeckoBundle settings);
+                                           EventDispatcher dispatcher, GeckoBundle initData);
 
         @WrapForJNI(dispatchTo = "proxy")
         public native void attachEditable(IGeckoEditableParent parent,
                                           GeckoEditableChild child);
 
         @WrapForJNI(calledFrom = "gecko")
         private synchronized void onReady(final @Nullable NativeQueue queue) {
             // onReady is called the first time the Gecko window is ready, with a null queue
@@ -662,17 +662,17 @@ public class GeckoSession extends LayerS
         }
 
         mWindow = window;
         mSettings = new GeckoSessionSettings(settings, this);
         mId = id;
 
         if (mWindow != null) {
             mWindow.transfer(mNativeQueue, mCompositor,
-                             mEventDispatcher, mSettings.asBundle());
+                             mEventDispatcher, createInitData());
 
             onWindowChanged(WINDOW_TRANSFER_IN, /* inProgress */ false);
         }
     }
 
     /* package */ void transferFrom(final GeckoSession session) {
         final boolean changing = (session.mWindow != null);
         if (changing) {
@@ -728,16 +728,22 @@ public class GeckoSession extends LayerS
     public boolean isOpen() {
         return mWindow != null;
     }
 
     /* package */ boolean isReady() {
         return mNativeQueue.isReady();
     }
 
+    private GeckoBundle createInitData() {
+        final GeckoBundle initData = new GeckoBundle(1);
+        initData.putBundle("settings", mSettings.toBundle());
+        return initData;
+    }
+
     /**
      * Opens the session.
      *
      * The session is in a 'closed' state when first created. Opening it creates
      * the underlying Gecko objects necessary to load a page, etc. Most GeckoSession
      * methods only take affect on an open session, and are queued until the session
      * is opened here. Opening a session is an asynchronous operation. You can check
      * the current state via isOpen().
@@ -762,26 +768,26 @@ public class GeckoSession extends LayerS
         final boolean isPrivate = mSettings.getBoolean(GeckoSessionSettings.USE_PRIVATE_MODE);
 
         mWindow = new Window(runtime, mNativeQueue);
 
         onWindowChanged(WINDOW_OPEN, /* inProgress */ true);
 
         if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
             Window.open(mWindow, mNativeQueue, mCompositor, mEventDispatcher,
-                        mSettings.asBundle(), mId, chromeUri, screenId, isPrivate);
+                        createInitData(), 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(),
+                GeckoBundle.class, createInitData(),
                 String.class, mId,
                 String.class, chromeUri,
                 screenId, isPrivate);
         }
 
         onWindowChanged(WINDOW_OPEN, /* inProgress */ false);
     }
 
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionSettings.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionSettings.java
@@ -160,18 +160,18 @@ public final class GeckoSessionSettings 
     }
 
     public String getString(final Key<String> key) {
         synchronized (mBundle) {
             return mBundle.getString(key.name);
         }
     }
 
-    /* package */ GeckoBundle asBundle() {
-        return mBundle;
+    /* package */ GeckoBundle toBundle() {
+        return new GeckoBundle(mBundle);
     }
 
     @Override
     public String toString() {
         return mBundle.toString();
     }
 
     @Override
--- a/widget/android/EventDispatcher.cpp
+++ b/widget/android/EventDispatcher.cpp
@@ -820,16 +820,23 @@ EventDispatcher::Dispatch(const char16_t
                           java::GeckoBundle::Param aData,
                           nsIAndroidEventCallback* aCallback)
 {
     nsDependentString event(aEvent);
 
     ListenersList* list = mListenersMap.Get(event);
     if (list) {
         dom::AutoJSAPI jsapi;
+        if (mDOMWindow) {
+            NS_ENSURE_TRUE(jsapi.Init(mDOMWindow->GetCurrentInnerWindow()),
+                           NS_ERROR_FAILURE);
+        } else {
+            NS_ENSURE_TRUE(jsapi.Init(xpc::PrivilegedJunkScope()),
+                           NS_ERROR_FAILURE);
+        }
         JS::RootedValue data(jsapi.cx());
         nsresult rv = UnboxData(/* Event */ nullptr, jsapi.cx(), aData, &data,
                                 /* BundleOnly */ true);
         NS_ENSURE_SUCCESS(rv, rv);
         return DispatchOnGecko(list, event, data, aCallback);
     }
 
     if (!mDispatcher) {
--- a/widget/android/nsIAndroidBridge.idl
+++ b/widget/android/nsIAndroidBridge.idl
@@ -66,17 +66,17 @@ interface nsIAndroidEventDispatcher : ns
   [implicit_jscontext]
   void unregisterListener(in nsIAndroidEventListener listener,
                           in jsval events);
 };
 
 [scriptable, uuid(60a78a94-6117-432f-9d49-304913a931c5)]
 interface nsIAndroidView : nsIAndroidEventDispatcher
 {
-  [implicit_jscontext] readonly attribute jsval settings;
+  [implicit_jscontext] readonly attribute jsval initData;
 };
 
 [scriptable, uuid(1beb70d3-70f3-4742-98cc-a3d301b26c0c)]
 interface nsIAndroidBridge : nsIAndroidEventDispatcher
 {
   attribute nsIAndroidBrowserApp browserApp;
   void contentDocumentChanged(in mozIDOMWindowProxy window);
   boolean isContentDocumentDisplayed(in mozIDOMWindowProxy window);
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -279,31 +279,31 @@ private:
 
 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::Object::Param aInitData,
                      jni::String::Param aId,
                      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 aQueue,
                   jni::Object::Param aCompositor,
                   jni::Object::Param aDispatcher,
-                  jni::Object::Param aSettings);
+                  jni::Object::Param aInitData);
 
     void AttachEditable(const GeckoSession::Window::LocalRef& inst,
                         jni::Object::Param aEditableParent,
                         jni::Object::Param aEditableChild);
 
     void OnReady(jni::Object::Param aQueue = nullptr);
 };
 
@@ -758,26 +758,24 @@ nsWindow::NativePtr<nsWindow::NPZCSuppor
 bool nsWindow::NPZCSupport::sNegateWheelScroll;
 
 NS_IMPL_ISUPPORTS(nsWindow::AndroidView,
                   nsIAndroidEventDispatcher,
                   nsIAndroidView)
 
 
 nsresult
-nsWindow::AndroidView::GetSettings(JSContext* aCx, JS::MutableHandleValue aOut)
+nsWindow::AndroidView::GetInitData(JSContext* aCx, JS::MutableHandleValue aOut)
 {
-    if (!mSettings) {
+    if (!mInitData) {
         aOut.setNull();
         return NS_OK;
     }
 
-    // Lock to prevent races with UI thread.
-    auto lock = mSettings.Lock();
-    return widget::EventDispatcher::UnboxBundle(aCx, mSettings, aOut);
+    return widget::EventDispatcher::UnboxBundle(aCx, mInitData, aOut);
 }
 
 /**
  * Compositor has some unique requirements for its native calls, so make it
  * separate from GeckoViewSupport.
  */
 class nsWindow::LayerViewSupport final
     : public LayerSession::Compositor::Natives<LayerViewSupport>
@@ -1166,17 +1164,17 @@ nsWindow::GeckoViewSupport::~GeckoViewSu
 }
 
 /* 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::Object::Param aInitData,
                                  jni::String::Param aId,
                                  jni::String::Param aChromeURI,
                                  int32_t aScreenId,
                                  bool aPrivateMode)
 {
     MOZ_ASSERT(NS_IsMainThread());
 
     AUTO_PROFILER_LABEL("nsWindow::GeckoViewSupport::Open", OTHER);
@@ -1193,17 +1191,17 @@ nsWindow::GeckoViewSupport::Open(const j
             url = NS_LITERAL_CSTRING("chrome://geckoview/content/geckoview.xul");
         }
     }
 
     // Prepare an nsIAndroidView to pass as argument to the window.
     RefPtr<AndroidView> androidView = new AndroidView();
     androidView->mEventDispatcher->Attach(
             java::EventDispatcher::Ref::From(aDispatcher), nullptr);
-    androidView->mSettings = java::GeckoBundle::Ref::From(aSettings);
+    androidView->mInitData = java::GeckoBundle::Ref::From(aInitData);
 
     nsAutoCString chromeFlags("chrome,dialog=0,resizable,scrollbars");
     if (aPrivateMode) {
         chromeFlags += ",private";
     }
     nsCOMPtr<mozIDOMWindowProxy> domWindow;
     ww->OpenWindow(nullptr, url.get(), aId->ToCString().get(), chromeFlags.get(),
                    androidView, getter_AddRefs(domWindow));
@@ -1221,17 +1219,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, aQueue, aCompositor, aDispatcher, aSettings);
+            sessionWindow, aQueue, aCompositor, aDispatcher, aInitData);
 
     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);
@@ -1255,17 +1253,17 @@ nsWindow::GeckoViewSupport::Close()
     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)
+                                     jni::Object::Param aInitData)
 {
     if (window.mNPZCSupport) {
         MOZ_ASSERT(window.mLayerViewSupport);
         window.mNPZCSupport.Detach();
     }
 
     if (window.mLayerViewSupport) {
         window.mLayerViewSupport.Detach();
@@ -1273,20 +1271,24 @@ nsWindow::GeckoViewSupport::Transfer(con
 
     auto compositor = LayerSession::Compositor::LocalRef(
             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);
 
     if (mIsReady) {
+        // We're in a transfer; update init-data and notify JS code.
+        window.mAndroidView->mInitData =
+                java::GeckoBundle::Ref::From(aInitData);
         OnReady(aQueue);
+        window.mAndroidView->mEventDispatcher->Dispatch(
+                u"GeckoView:UpdateInitData");
     }
 
     DispatchToUiThread(
             "GeckoViewSupport::Transfer",
             [compositor = LayerSession::Compositor::GlobalRef(compositor)] {
                 compositor->OnCompositorAttached();
             });
 
--- a/widget/android/nsWindow.h
+++ b/widget/android/nsWindow.h
@@ -161,17 +161,17 @@ private:
 
         AndroidView() {}
 
         NS_DECL_ISUPPORTS
         NS_DECL_NSIANDROIDVIEW
 
         NS_FORWARD_NSIANDROIDEVENTDISPATCHER(mEventDispatcher->)
 
-        mozilla::java::GeckoBundle::GlobalRef mSettings;
+        mozilla::java::GeckoBundle::GlobalRef mInitData;
     };
 
     RefPtr<AndroidView> mAndroidView;
 
     class LayerViewSupport;
     // Object that implements native LayerView calls.
     // Owned by the Java LayerView instance.
     NativePtr<LayerViewSupport> mLayerViewSupport;