Bug 1413698 - 3. Recorganize GeckoViewSettings; r=snorp draft
authorJim Chen <nchen@mozilla.com>
Mon, 06 Nov 2017 14:54:09 -0500
changeset 693758 537689d93202e4201e879bae9e073ceb4489190b
parent 693757 60e045f6f1e6a70dcabc03f5a32cae5576c2edd8
child 693759 596866e9121265bf0529648926a71d1dfe3b2976
push id87902
push userbmo:nchen@mozilla.com
push dateMon, 06 Nov 2017 19:55:12 +0000
reviewerssnorp
bugs1413698
milestone58.0a1
Bug 1413698 - 3. Recorganize GeckoViewSettings; r=snorp * Move the chromeUri and screenId settings to GeckoViewSettings. * Add a private data-dir setting that the debugger-socket-dir setting falls backs to. Set the data-dir setting inside `GeckoSession.openWindow`. * Add optional init-only and values properties for settings. * Use integer constants for display-mode setting. MozReview-Commit-ID: HgJg0t0oade
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSession.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoViewSettings.java
mobile/android/modules/geckoview/GeckoViewRemoteDebugger.jsm
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSession.java
@@ -338,56 +338,50 @@ public class GeckoSession implements Par
             if ("GeckoView:Prompt".equals(event)) {
                 handlePromptEvent(GeckoSession.this, message, callback);
             }
         }
     }
 
     protected Window mWindow;
     private GeckoViewSettings mSettings;
-    private String mChromeUri;
-    private int mScreenId = 0; // default to the primary screen
 
     public GeckoSession() {
         this(/* settings */ null);
     }
 
     public GeckoSession(final GeckoViewSettings settings) {
         if (settings == null) {
-            mSettings = new GeckoViewSettings(mEventDispatcher);
+            mSettings = new GeckoViewSettings(this);
         } else {
-            mSettings = new GeckoViewSettings(settings, mEventDispatcher);
+            mSettings = new GeckoViewSettings(settings, this);
         }
 
         mListener.registerListeners();
     }
 
     /* package */ void transferFrom(final GeckoSession session) {
         if (isOpen()) {
             throw new IllegalStateException("Session is open");
         }
+
         mWindow = session.mWindow;
+        mSettings = new GeckoViewSettings(session.mSettings, this);
         session.mWindow = null;
-
-        mSettings = new GeckoViewSettings(session.mSettings, getEventDispatcher());
-        mChromeUri = session.mChromeUri;
-        mScreenId = session.mScreenId;
     }
 
     @Override // Parcelable
     public int describeContents() {
         return 0;
     }
 
     @Override // Parcelable
     public void writeToParcel(Parcel out, int flags) {
         out.writeStrongInterface(mWindow);
         out.writeParcelable(mSettings, flags);
-        out.writeString(mChromeUri);
-        out.writeInt(mScreenId);
     }
 
     // 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");
         }
 
@@ -397,20 +391,17 @@ public class GeckoSession implements Par
         if (window instanceof Window) {
             mWindow = (Window) window;
         } else {
             mWindow = null;
         }
 
         final GeckoViewSettings settings =
                 source.readParcelable(getClass().getClassLoader());
-        mSettings = new GeckoViewSettings(settings, getEventDispatcher());
-
-        mChromeUri = source.readString();
-        mScreenId = source.readInt();
+        mSettings = new GeckoViewSettings(settings, this);
     }
 
     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;
@@ -447,86 +438,54 @@ public class GeckoSession implements Par
         }
 
         final int flags = multiprocess ? GeckoThread.FLAG_PRELOAD_CHILD : 0;
         if (GeckoThread.initMainProcess(/* profile */ null, geckoArgs, flags)) {
             GeckoThread.launch();
         }
     }
 
-    /**
-     * Return the URI of the underlying chrome window opened or to be opened, or null if
-     * using the default GeckoSession URI.
-     *
-     * @return Current chrome URI or null.
-     */
-    public String getChromeUri() {
-        return mChromeUri;
-    }
-
-    /**
-     * Set the URI of the underlying chrome window to be opened, or null to use the
-     * default GeckoSession URI. Can only be called before the chrome window is opened during
-     * {@link #onAttachedToWindow}.
-     *
-     * @param uri New chrome URI or null.
-     */
-    public void setChromeUri(final String uri) {
-        if (isOpen()) {
-            throw new IllegalStateException("Session is open");
-        }
-        mChromeUri = uri;
-    }
-
-    public int getScreenId() {
-        return mScreenId;
-    }
-
-    public void setScreenId(final int id) {
-        if (isOpen()) {
-            throw new IllegalStateException("Session is open");
-        }
-        mScreenId = id;
-    }
-
     public boolean isOpen() {
         return mWindow != null;
     }
 
     public void openWindow(final Context appContext) {
         if (isOpen()) {
             throw new IllegalStateException("Session is open");
         }
 
         if (!GeckoThread.isLaunched()) {
             final boolean multiprocess =
                     mSettings.getBoolean(GeckoViewSettings.USE_MULTIPROCESS);
             preload(appContext, /* geckoArgs */ null, multiprocess);
         }
 
-        if (mSettings.getString(GeckoViewSettings.DEBUGGER_SOCKET_DIR) == null) {
-            mSettings.setString(GeckoViewSettings.DEBUGGER_SOCKET_DIR,
+        if (mSettings.getString(GeckoViewSettings.DATA_DIR) == null) {
+            mSettings.setString(GeckoViewSettings.DATA_DIR,
                                 appContext.getApplicationInfo().dataDir);
         }
 
+        final String chromeUri = mSettings.getString(GeckoViewSettings.CHROME_URI);
+        final int screenId = mSettings.getInt(GeckoViewSettings.SCREEN_ID);
+        final boolean isPrivate = mSettings.getBoolean(GeckoViewSettings.USE_PRIVATE_MODE);
+
         mWindow = new Window(mNativeQueue);
 
         if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
             Window.open(mWindow, mEventDispatcher, mSettings.asBundle(),
-                        mChromeUri, mScreenId,
-                        mSettings.getBoolean(GeckoViewSettings.USE_PRIVATE_MODE));
+                        chromeUri, screenId, isPrivate);
         } else {
             GeckoThread.queueNativeCallUntil(
                 GeckoThread.State.PROFILE_READY,
                 Window.class, "open",
                 Window.class, mWindow,
                 EventDispatcher.class, mEventDispatcher,
                 GeckoBundle.class, mSettings.asBundle(),
-                String.class, mChromeUri,
-                mScreenId, mSettings.getBoolean(GeckoViewSettings.USE_PRIVATE_MODE));
+                String.class, chromeUri,
+                screenId, isPrivate);
         }
     }
 
     public void attachView(final GeckoView view) {
         if (view == null) {
             return;
         }
 
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoViewSettings.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoViewSettings.java
@@ -7,160 +7,184 @@
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.util.GeckoBundle;
 
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
 
+import java.util.Arrays;
+import java.util.Collection;
+
 public final class GeckoViewSettings implements Parcelable {
     private static final String LOGTAG = "GeckoViewSettings";
     private static final boolean DEBUG = false;
 
-    private static class Key<T> {
-        private final String text;
+    // This needs to match nsIDocShell.idl
+    public static final int DISPLAY_MODE_BROWSER = 0;
+    public static final int DISPLAY_MODE_MINIMAL_UI = 1;
+    public static final int DISPLAY_MODE_STANDALONE = 2;
+    public static final int DISPLAY_MODE_FULLSCREEN = 3;
 
-        public Key(final String text) {
-            this.text = text;
+    private static class Key<T> {
+        public final String name;
+        public final boolean initOnly;
+        public final Collection<T> values;
+
+        public Key(final String name) {
+            this(name, /* initOnly */ false, /* values */ null);
+        }
+
+        public Key(final String name, final boolean initOnly, final Collection<T> values) {
+            this.name = name;
+            this.initOnly = initOnly;
+            this.values = values;
         }
     }
 
-    public enum DisplayMode {
-        // This needs to match nsIDocShell.idl
-        BROWSER(0),
-        MINIMAL_UI(1),
-        STANDALONE(2),
-        FULLSCREEN(3);
-
-        private final int mMode;
-
-        DisplayMode(int mode) {
-            mMode = mode;
-        }
-
-        public int value() {
-            return mMode;
-        }
-    }
+    /**
+     * Key to set the chrome window URI, or null to use default URI.
+     * Read-only once session is open.
+     */
+    public static final Key<String> CHROME_URI =
+        new Key<String>("chromeUri", /* initOnly */ true, /* values */ null);
+    /**
+     * Key to set the window screen ID, or 0 to use default ID.
+     * Read-only once session is open.
+     */
+    public static final Key<Integer> SCREEN_ID =
+        new Key<Integer>("screenId", /* initOnly */ true, /* values */ null);
 
     /*
-     * Key to enabled and disable tracking protection.
+     * Key to enable and disable tracking protection.
      */
     public static final Key<Boolean> USE_TRACKING_PROTECTION =
         new Key<Boolean>("useTrackingProtection");
     /*
-     * Key to enabled and disable private mode browsing.
+     * Key to enable and disable private mode browsing.
+     * Read-only once session is open.
      */
     public static final Key<Boolean> USE_PRIVATE_MODE =
-        new Key<Boolean>("usePrivateMode");
+        new Key<Boolean>("usePrivateMode", /* initOnly */ true, /* values */ null);
 
     /*
-     * Key to enabled and disable multiprocess browsing (e10s).
-     * Note: can only be set during GeckoView initialization, changes during an
-     * active GeckoView session will be ignored.
+     * Key to enable and disable multiprocess browsing (e10s).
+     * Read-only once session is open.
      */
     public static final Key<Boolean> USE_MULTIPROCESS =
-        new Key<Boolean>("useMultiprocess");
+        new Key<Boolean>("useMultiprocess", /* initOnly */ true, /* values */ null);
 
     /*
      * Key to specify which display-mode we should use
      */
     public static final Key<Integer> DISPLAY_MODE =
-        new Key<Integer>("displayMode");
+        new Key<Integer>("displayMode", /* initOnly */ false,
+                         Arrays.asList(DISPLAY_MODE_BROWSER, DISPLAY_MODE_MINIMAL_UI,
+                                       DISPLAY_MODE_STANDALONE, DISPLAY_MODE_FULLSCREEN));
 
     public static final Key<Boolean> USE_REMOTE_DEBUGGER =
         new Key<Boolean>("useRemoteDebugger");
 
     public static final Key<String> DEBUGGER_SOCKET_DIR =
         new Key<String>("debuggerSocketDir");
 
-    private final EventDispatcher mEventDispatcher;
+    /* package */ static final Key<String> DATA_DIR =
+        new Key<String>("dataDir", /* initOnly */ true, /* values */ null);
+
+    private final GeckoSession mSession;
     private final GeckoBundle mBundle;
 
     public GeckoViewSettings() {
         this(null);
     }
 
-    /* package */ GeckoViewSettings(EventDispatcher eventDispatcher) {
-        mEventDispatcher = eventDispatcher;
+    /* package */ GeckoViewSettings(final GeckoSession session) {
+        mSession = session;
         mBundle = new GeckoBundle();
 
-        setBoolean(USE_TRACKING_PROTECTION, false);
-        setBoolean(USE_PRIVATE_MODE, false);
-        setBoolean(USE_MULTIPROCESS, true);
-        setInt(DISPLAY_MODE, DisplayMode.BROWSER.value());
-        setBoolean(USE_REMOTE_DEBUGGER, false);
-        // Set in GeckoView.init().
-        setString(DEBUGGER_SOCKET_DIR, ".");
+        mBundle.putString(CHROME_URI.name, null);
+        mBundle.putInt(SCREEN_ID.name, 0);
+        mBundle.putBoolean(USE_TRACKING_PROTECTION.name, false);
+        mBundle.putBoolean(USE_PRIVATE_MODE.name, false);
+        mBundle.putBoolean(USE_MULTIPROCESS.name, true);
+        mBundle.putInt(DISPLAY_MODE.name, DISPLAY_MODE_BROWSER);
+        mBundle.putBoolean(USE_REMOTE_DEBUGGER.name, false);
+        mBundle.putString(DEBUGGER_SOCKET_DIR.name, null);
     }
 
-    /* package */ GeckoViewSettings(GeckoViewSettings settings, EventDispatcher eventDispatcher) {
+    /* package */ GeckoViewSettings(final GeckoViewSettings settings,
+                                    final GeckoSession session) {
+        mSession = session;
         mBundle = new GeckoBundle(settings.mBundle);
-        mEventDispatcher = eventDispatcher;
     }
 
-    public void setBoolean(final Key<Boolean> key, boolean value) {
+    public void setBoolean(final Key<Boolean> key, final boolean value) {
         synchronized (mBundle) {
-            final Object old = mBundle.get(key.text);
-            if (old != null && old.equals(value)) {
-                return;
+            if (valueChangedLocked(key, value)) {
+                mBundle.putBoolean(key.name, value);
+                dispatchUpdate();
             }
-            mBundle.putBoolean(key.text, value);
         }
-        dispatchUpdate();
     }
 
     public boolean getBoolean(final Key<Boolean> key) {
         synchronized (mBundle) {
-            return mBundle.getBoolean(key.text);
+            return mBundle.getBoolean(key.name);
         }
     }
 
-    public void setInt(final Key<Integer> key, int value) {
+    public void setInt(final Key<Integer> key, final int value) {
         synchronized (mBundle) {
-            final Object old = mBundle.get(key.text);
-            if (old != null && old.equals(value)) {
-                return;
+            if (valueChangedLocked(key, value)) {
+                mBundle.putInt(key.name, value);
+                dispatchUpdate();
             }
-            mBundle.putInt(key.text, value);
         }
-        dispatchUpdate();
     }
 
     public int getInt(final Key<Integer> key) {
         synchronized (mBundle) {
-            return mBundle.getInt(key.text);
+            return mBundle.getInt(key.name);
         }
     }
 
     public void setString(final Key<String> key, final String value) {
         synchronized (mBundle) {
-            final Object old = mBundle.get(key.text);
-            if (old != null && old.equals(value)) {
-                return;
+            if (valueChangedLocked(key, value)) {
+                mBundle.putString(key.name, value);
+                dispatchUpdate();
             }
-            mBundle.putString(key.text, value);
         }
-        dispatchUpdate();
     }
 
     public String getString(final Key<String> key) {
         synchronized (mBundle) {
-            return mBundle.getString(key.text);
+            return mBundle.getString(key.name);
         }
     }
 
     /* package */ GeckoBundle asBundle() {
         return mBundle;
     }
 
+    private <T> boolean valueChangedLocked(final Key<T> key, T value) {
+        if (key.initOnly && mSession != null && mSession.isOpen()) {
+            throw new IllegalStateException("Read-only property");
+        } else if (key.values != null && !key.values.contains(value)) {
+            throw new IllegalArgumentException("Invalid value");
+        }
+
+        final Object old = mBundle.get(key.name);
+        return (old != value) && (old == null || !old.equals(value));
+    }
+
     private void dispatchUpdate() {
-        if (mEventDispatcher != null) {
-            mEventDispatcher.dispatch("GeckoView:UpdateSettings", null);
+        if (mSession != null) {
+            mSession.getEventDispatcher().dispatch("GeckoView:UpdateSettings", null);
         }
     }
 
     @Override // Parcelable
     public int describeContents() {
         return 0;
     }
 
--- a/mobile/android/modules/geckoview/GeckoViewRemoteDebugger.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewRemoteDebugger.jsm
@@ -51,17 +51,17 @@ class GeckoViewRemoteDebugger extends Ge
       DebuggerServer.allowChromeProcess = true;
     }
     this._isEnabled = true;
     this._usbDebugger.stop();
 
     let windowId = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
                               .getInterface(Ci.nsIDOMWindowUtils)
                               .outerWindowID;
-    let portOrPath = this.settings.debuggerSocketDir +
+    let portOrPath = (this.settings.debuggerSocketDir || this.settings.dataDir) +
                      "/firefox-debugger-socket-" +
                      windowId;
     this._usbDebugger.start(portOrPath);
   }
 
   unregister() {
     this._isEnabled = false;
     this._usbDebugger.stop();