Bug 1460045 - Pass module enabled states through init-data; r?esawin draft
authorJim Chen <nchen@mozilla.com>
Fri, 11 May 2018 10:53:54 -0400
changeset 794210 b516aa228cd4a973d05c2a75075cff87f6cfe733
parent 794209 97a1c7d0f26c0726661c86b2e920172b1ff7d1cf
child 794211 ffb90ad2c1351bbf45a170975083444fa5832c4d
push id109609
push userbmo:nchen@mozilla.com
push dateFri, 11 May 2018 14:54:33 +0000
reviewersesawin
bugs1460045
milestone62.0a1
Bug 1460045 - Pass module enabled states through init-data; r?esawin During session initialization, pass in the enabled state of all modules through the init-data, and use that to enable modules if necessary. This avoids a race condition where we enable modules too late on startup due to event queuing. MozReview-Commit-ID: I0rvq6UoOVh
mobile/android/chrome/geckoview/geckoview.js
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionHandler.java
--- a/mobile/android/chrome/geckoview/geckoview.js
+++ b/mobile/android/chrome/geckoview/geckoview.js
@@ -35,16 +35,17 @@ var ModuleManager = {
     this._frozenSettings = Object.freeze(Object.assign({}, this._settings));
 
     const self = this;
     this._modules = new Map((function* () {
       for (const module of aModules) {
         yield [
           module.name,
           new ModuleInfo({
+            enabled: !!initData.modules[module.name],
             manager: self,
             ...module,
           }),
         ];
       }
     })());
 
     WindowEventDispatcher.registerListener(this, [
@@ -98,19 +99,27 @@ var ModuleManager = {
         const module = this._modules.get(aData.module);
         if (module) {
           module.enabled = aData.enabled;
         }
         break;
       }
 
       case "GeckoView:UpdateInitData": {
-        // Replace all sett_onSettingsUpdateings during a transfer.
+        // Replace all settings during a transfer.
         const initData = this._initData;
         this._updateSettings(initData.settings);
+
+        // Update module enabled states.
+        for (const name in initData.modules) {
+          const module = this._modules.get(name);
+          if (module) {
+            module.enabled = initData.modules[name];
+          }
+        }
         break;
       }
 
       case "GeckoView:UpdateSettings": {
         this._updateSettings(aData);
         break;
       }
     }
@@ -119,31 +128,34 @@ var ModuleManager = {
 
 /**
  * ModuleInfo is the structure used by ModuleManager to represent individual
  * modules. It is responsible for loading the module JSM file if necessary,
  * and it acts as the intermediary between ModuleManager and the module
  * object that extends GeckoViewModule.
  */
 class ModuleInfo {
-  constructor({manager, name, resource}) {
+  constructor({enabled, manager, name, resource}) {
     this._manager = manager;
     this._name = name;
 
     const scope = {};
     const global = ChromeUtils.import(resource, scope);
     const tag = name.replace("GeckoView", "GeckoView.");
     GeckoViewUtils.initLogging(tag, global);
 
     this._impl = new scope[name](this);
     this._enabled = false;
+    // Only enable once we performed initialization.
+    this._enabledOnInit = enabled;
   }
 
   onInit() {
     this._impl.onInit();
+    this.enabled = this._enabledOnInit;
   }
 
   get manager() {
     return this._manager;
   }
 
   get name() {
     return this._name;
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
@@ -737,18 +737,24 @@ public class GeckoSession extends LayerS
         return mWindow != null;
     }
 
     /* package */ boolean isReady() {
         return mNativeQueue.isReady();
     }
 
     private GeckoBundle createInitData() {
-        final GeckoBundle initData = new GeckoBundle(1);
+        final GeckoBundle initData = new GeckoBundle(2);
         initData.putBundle("settings", mSettings.toBundle());
+
+        final GeckoBundle modules = new GeckoBundle(mSessionHandlers.length);
+        for (final GeckoSessionHandler<?> handler : mSessionHandlers) {
+            modules.putBoolean(handler.getName(), handler.isEnabled());
+        }
+        initData.putBundle("modules", modules);
         return initData;
     }
 
     /**
      * Opens the session.
      *
      * Call this when you are ready to use a GeckoSession instance.
      *
@@ -823,25 +829,16 @@ public class GeckoSession extends LayerS
 
         onWindowChanged(WINDOW_CLOSE, /* inProgress */ false);
     }
 
     private void onWindowChanged(int change, boolean inProgress) {
         if ((change == WINDOW_OPEN || change == WINDOW_TRANSFER_IN) && !inProgress) {
             mTextInput.onWindowChanged(mWindow);
         }
-
-        if (change == WINDOW_CLOSE) {
-            // Detach when window is closing, and reattach immediately after window is closed.
-            // We reattach immediate after closing because we want any actions performed while the
-            // session is closed to be properly queued, until the session is open again.
-            for (final GeckoSessionHandler<?> handler : mSessionHandlers) {
-                handler.setSessionIsReady(this, !inProgress);
-            }
-        }
     }
 
     /**
      * Get the SessionTextInput instance for this session. May be called on any thread.
      *
      * @return SessionTextInput instance.
      */
     public @NonNull SessionTextInput getTextInput() {
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionHandler.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionHandler.java
@@ -64,38 +64,42 @@ import android.util.Log;
             unregister(session);
         }
 
         mDelegate = delegate;
 
         if (!mAlwaysListen && settingNewDelegate) {
             register(session);
         }
+
+        // If session is not open, we will update module state during session opening.
+        if (!session.isOpen()) {
+            return;
+        }
+
+        final GeckoBundle msg = new GeckoBundle(2);
+        msg.putString("module", mModuleName);
+        msg.putBoolean("enabled", isEnabled());
+        session.getEventDispatcher().dispatch("GeckoView:UpdateModuleState", msg);
     }
 
     private void unregister(final GeckoSession session) {
-        setSessionIsReady(session, /* ready */ false);
         session.getEventDispatcher().unregisterUiThreadListener(this, mEvents);
     }
 
     private void register(final GeckoSession session) {
         session.getEventDispatcher().registerUiThreadListener(this, mEvents);
-        setSessionIsReady(session, /* ready */ true);
     }
 
-    public void setSessionIsReady(final GeckoSession session, final boolean ready) {
-        // If not enabled, we don't need Gecko to register/unregister.
-        if (!mAlwaysListen && mDelegate == null) {
-            return;
-        }
+    public String getName() {
+        return mModuleName;
+    }
 
-        final GeckoBundle msg = new GeckoBundle(2);
-        msg.putString("module", mModuleName);
-        msg.putBoolean("enabled", ready);
-        session.getEventDispatcher().dispatch("GeckoView:UpdateModuleState", msg);
+    public boolean isEnabled() {
+        return mDelegate != null;
     }
 
     @Override
     public void handleMessage(final String event, final GeckoBundle message,
                               final EventCallback callback) {
         if (DEBUG) {
             Log.d(LOGTAG, mModuleName + " handleMessage: event = " + event);
         }