Bug 1500155 - Part 4 - Add Builder to `GeckoSessionSettings` to handle setting of init only fields. r=snorp
☠☠ backed out by f7c35130959c ☠ ☠
authorEmily Toop <etoop@mozilla.com>
Wed, 12 Dec 2018 13:18:39 +0000
changeset 450251 4dd0b1c3524cafe7cfe7ba5d10105324b4906895
parent 450250 0d03623732b6f0ee20cb02c7446ae1086c48b13f
child 450252 c4bc2e839a9e28b83802caf99d2ecf5eddd14e65
push id74573
push useretoop@mozilla.com
push dateWed, 12 Dec 2018 13:20:44 +0000
treeherderautoland@0f66513c5f23 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs1500155
milestone66.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 1500155 - Part 4 - Add Builder to `GeckoSessionSettings` to handle setting of init only fields. r=snorp * Make setters for init only fields protected. * Remove tests that ensure that init only fields throw an error when set on the fly as this is no longer possible. * Update tests to use builder when init-ing settings. * Update API doc to reflect new public API. Differential Revision: https://phabricator.services.mozilla.com/D13420
mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
mobile/android/base/java/org/mozilla/gecko/VirtualPresentation.java
mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java
mobile/android/geckoview/api.txt
mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt
mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/GeckoSessionTestRuleTest.kt
mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/NavigationDelegateTest.kt
mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/SessionLifecycleTest.kt
mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/crash/RemoteGeckoService.kt
mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionSettings.java
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionAccessibility.java
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -1082,19 +1082,20 @@ public abstract class GeckoApp extends G
         setContentView(getLayout());
 
         // Set up Gecko layout.
         mRootLayout = (RelativeLayout) findViewById(R.id.root_layout);
         mGeckoLayout = (RelativeLayout) findViewById(R.id.gecko_layout);
         mMainLayout = (RelativeLayout) findViewById(R.id.main_layout);
         mLayerView = (GeckoView) findViewById(R.id.layer_view);
 
-        final GeckoSession session = new GeckoSession();
-        session.getSettings().setString(GeckoSessionSettings.CHROME_URI,
-                                        "chrome://browser/content/browser.xul");
+        final GeckoSession session = new GeckoSession(
+                new GeckoSessionSettings.Builder()
+                        .chromeUri("chrome://browser/content/browser.xul")
+                        .build());
         session.setContentDelegate(this);
 
         // If the view already has a session, we need to ensure it is closed.
         if (mLayerView.getSession() != null) {
             mLayerView.getSession().close();
         }
         mLayerView.setSession(session, GeckoApplication.getRuntime());
         mLayerView.setOverScrollMode(View.OVER_SCROLL_NEVER);
--- a/mobile/android/base/java/org/mozilla/gecko/VirtualPresentation.java
+++ b/mobile/android/base/java/org/mozilla/gecko/VirtualPresentation.java
@@ -40,20 +40,20 @@ class VirtualPresentation extends CastPr
         super.onCreate(savedInstanceState);
 
         /*
          * NOTICE: The context get from getContext() is different to the context
          * of the application. Presentaion has its own context to get correct
          * resources.
          */
 
-        final GeckoSession session = new GeckoSession();
-        session.getSettings().setString(GeckoSessionSettings.CHROME_URI,
-                PRESENTATION_VIEW_URI + "#" + deviceId);
-        session.getSettings().setInt(GeckoSessionSettings.SCREEN_ID, screenId);
+        final GeckoSession session = new GeckoSession(new GeckoSessionSettings.Builder()
+                .chromeUri(PRESENTATION_VIEW_URI + "#" + deviceId)
+                .screenId(screenId)
+                .build());
 
         // Create new GeckoView
         view = new GeckoView(getContext());
         view.setSession(session, GeckoApplication.ensureRuntime(getContext()));
         view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
                 LayoutParams.MATCH_PARENT));
 
         // Create new layout to put the GeckoView
--- a/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
@@ -118,18 +118,19 @@ public class CustomTabsActivity extends 
 
         actionBarPresenter = new ActionBarPresenter(actionBar, getActionBarTextColor());
         actionBarPresenter.displayUrlOnly(intent.getDataString());
         actionBarPresenter.setBackgroundColor(IntentUtil.getToolbarColor(intent), getWindow());
         actionBarPresenter.setTextLongClickListener(new UrlCopyListener());
 
         mGeckoView = (GeckoView) findViewById(R.id.gecko_view);
 
-        final GeckoSessionSettings settings = new GeckoSessionSettings();
-        settings.setBoolean(GeckoSessionSettings.USE_MULTIPROCESS, false);
+        final GeckoSessionSettings settings = new GeckoSessionSettings.Builder()
+                .useMultiprocess(false)
+                .build();
         mGeckoSession = new GeckoSession(settings);
         mGeckoSession.setNavigationDelegate(this);
         mGeckoSession.setProgressDelegate(this);
         mGeckoSession.setContentDelegate(this);
 
         mGeckoView.setSession(mGeckoSession, GeckoApplication.ensureRuntime(this));
 
         mPromptService = new PromptService(this, mGeckoView.getEventDispatcher());
--- a/mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java
@@ -93,18 +93,19 @@ public class WebAppActivity extends AppC
         } else {
             Telemetry.sendUIEvent(TelemetryContract.Event.PWA, TelemetryContract.Method.HOMESCREEN);
         }
 
         super.onCreate(savedInstanceState);
         setContentView(R.layout.webapp_activity);
         mGeckoView = (GeckoView) findViewById(R.id.pwa_gecko_view);
 
-        final GeckoSessionSettings settings = new GeckoSessionSettings();
-        settings.setBoolean(GeckoSessionSettings.USE_MULTIPROCESS, false);
+        final GeckoSessionSettings settings = new GeckoSessionSettings.Builder()
+                .useMultiprocess(false)
+                .build();
         mGeckoSession = new GeckoSession(settings);
         mGeckoView.setSession(mGeckoSession, GeckoApplication.ensureRuntime(this));
 
         mGeckoSession.setNavigationDelegate(this);
         mGeckoSession.setContentDelegate(this);
         mGeckoSession.setProgressDelegate(new GeckoSession.ProgressDelegate() {
             @Override
             public void onPageStart(GeckoSession session, String url) {
@@ -314,17 +315,17 @@ public class WebAppActivity extends AppC
                 mode = GeckoSessionSettings.DISPLAY_MODE_MINIMAL_UI;
                 break;
             case "browser":
             default:
                 mode = GeckoSessionSettings.DISPLAY_MODE_BROWSER;
                 break;
         }
 
-        mGeckoSession.getSettings().setInt(GeckoSessionSettings.DISPLAY_MODE, mode);
+        mGeckoSession.getSettings().setDisplayMode(mode);
     }
 
     @Override // GeckoSession.NavigationDelegate
     public void onLocationChange(GeckoSession session, String url) {
     }
 
     @Override // GeckoSession.NavigationDelegate
     public void onCanGoBack(GeckoSession session, boolean canGoBack) {
--- a/mobile/android/geckoview/api.txt
+++ b/mobile/android/geckoview/api.txt
@@ -661,52 +661,65 @@ package org.mozilla.geckoview {
     field public final java.lang.String uri;
   }
 
   public final class GeckoSessionSettings implements android.os.Parcelable {
     ctor public GeckoSessionSettings();
     ctor public GeckoSessionSettings(org.mozilla.geckoview.GeckoSessionSettings);
     method public int describeContents();
     method public boolean equals(java.lang.Object);
+    method public boolean getAllowJavascript();
     method public java.lang.String getChromeUri();
     method public int getDisplayMode();
+    method public boolean getFullAccessibilityTree();
     method public int getScreenId();
+    method public boolean getSuspendMediaWhenInactive();
+    method public boolean getUseMultiprocess();
+    method public boolean getUsePrivateMode();
+    method public boolean getUseTrackingProtection();
     method public int getUserAgentMode();
     method public java.lang.String getUserAgentOverride();
     method public int hashCode();
-    method public boolean isAllowJavascript();
-    method public boolean isFullAccessibilityTree();
-    method public boolean isSuspendMediaWhenInactive();
-    method public boolean isUseMultiprocess();
-    method public boolean isUsePrivateMode();
-    method public boolean isUseTrackingProtection();
     method public void readFromParcel(android.os.Parcel);
     method public void setAllowJavascript(boolean);
-    method public void setChromeUri(java.lang.String);
     method public void setDisplayMode(int);
     method public void setFullAccessibilityTree(boolean);
-    method public void setScreenId(int);
     method public void setSuspendMediaWhenInactive(boolean);
-    method public void setUseMultiprocess(boolean);
-    method public void setUsePrivateMode(boolean);
     method public void setUseTrackingProtection(boolean);
     method public void setUserAgentMode(int);
     method public void setUserAgentOverride(java.lang.String);
     method public java.lang.String toString();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<org.mozilla.geckoview.GeckoSessionSettings> CREATOR;
     field public static final int DISPLAY_MODE_BROWSER = 0;
     field public static final int DISPLAY_MODE_FULLSCREEN = 3;
     field public static final int DISPLAY_MODE_MINIMAL_UI = 1;
     field public static final int DISPLAY_MODE_STANDALONE = 2;
     field public static final int USER_AGENT_MODE_DESKTOP = 1;
     field public static final int USER_AGENT_MODE_MOBILE = 0;
     field public static final int USER_AGENT_MODE_VR = 2;
   }
 
+  public static final class GeckoSessionSettings.Builder {
+    ctor public Builder();
+    ctor public Builder(org.mozilla.geckoview.GeckoSessionSettings);
+    method public org.mozilla.geckoview.GeckoSessionSettings.Builder allowJavascript(boolean);
+    method public org.mozilla.geckoview.GeckoSessionSettings build();
+    method public org.mozilla.geckoview.GeckoSessionSettings.Builder chromeUri(java.lang.String);
+    method public org.mozilla.geckoview.GeckoSessionSettings.Builder displayMode(int);
+    method public org.mozilla.geckoview.GeckoSessionSettings.Builder fullAccessibilityTree(boolean);
+    method public org.mozilla.geckoview.GeckoSessionSettings.Builder screenId(int);
+    method public org.mozilla.geckoview.GeckoSessionSettings.Builder suspendMediaWhenInactive(boolean);
+    method public org.mozilla.geckoview.GeckoSessionSettings.Builder useMultiprocess(boolean);
+    method public org.mozilla.geckoview.GeckoSessionSettings.Builder usePrivateMode(boolean);
+    method public org.mozilla.geckoview.GeckoSessionSettings.Builder useTrackingProtection(boolean);
+    method public org.mozilla.geckoview.GeckoSessionSettings.Builder userAgentMode(int);
+    method public org.mozilla.geckoview.GeckoSessionSettings.Builder userAgentOverride(java.lang.String);
+  }
+
   public static class GeckoSessionSettings.Key<T> {
   }
 
   public class GeckoView extends android.widget.FrameLayout {
     ctor public GeckoView(android.content.Context);
     ctor public GeckoView(android.content.Context, android.util.AttributeSet);
     method public void autofill(android.util.SparseArray<android.view.autofill.AutofillValue>);
     method @android.support.annotation.UiThread public void coverUntilFirstPaint(int);
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt
@@ -3,17 +3,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.geckoview.test
 
 import android.os.Parcel
 import android.support.test.InstrumentationRegistry
 import org.mozilla.geckoview.GeckoSession
-import org.mozilla.geckoview.GeckoSessionSettings
 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule
 
 import org.hamcrest.Matcher
 import org.hamcrest.Matchers
 import org.junit.Assume.assumeThat
 import org.junit.Rule
 import org.junit.rules.ErrorCollector
 
@@ -68,17 +67,17 @@ open class BaseSessionTest(noErrorCollec
 
     fun <T> forEachCall(vararg values: T): T = sessionRule.forEachCall(*values)
 
     fun getTestBytes(path: String) =
             InstrumentationRegistry.getTargetContext().resources.assets
                     .open(path.removePrefix("/assets/")).readBytes()
 
     val GeckoSession.isRemote
-        get() = this.settings.isUseMultiprocess()
+        get() = this.settings.getUseMultiprocess()
 
     fun createTestUrl(path: String) =
             GeckoSessionTestRule.APK_URI_PREFIX + path.removePrefix("/")
 
     fun GeckoSession.loadTestPath(path: String) =
             this.loadUri(createTestUrl(path))
 
     inline fun GeckoSession.toParcel(lambda: (Parcel) -> Unit) {
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/GeckoSessionTestRuleTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/GeckoSessionTestRuleTest.kt
@@ -43,26 +43,26 @@ class GeckoSessionTestRuleTest : BaseSes
     }
 
     @Setting.List(Setting(key = Setting.Key.USE_PRIVATE_MODE, value = "true"),
                   Setting(key = Setting.Key.DISPLAY_MODE, value = "DISPLAY_MODE_MINIMAL_UI"),
                   Setting(key = Setting.Key.ALLOW_JAVASCRIPT, value = "false"))
     @Setting(key = Setting.Key.USE_TRACKING_PROTECTION, value = "true")
     @Test fun settingsApplied() {
         assertThat("USE_PRIVATE_MODE should be set",
-                   sessionRule.session.settings.isUsePrivateMode,
+                   sessionRule.session.settings.usePrivateMode,
                    equalTo(true))
         assertThat("DISPLAY_MODE should be set",
                    sessionRule.session.settings.displayMode,
                    equalTo(GeckoSessionSettings.DISPLAY_MODE_MINIMAL_UI))
         assertThat("USE_TRACKING_PROTECTION should be set",
-                   sessionRule.session.settings.isUseTrackingProtection,
+                   sessionRule.session.settings.useTrackingProtection,
                    equalTo(true))
         assertThat("ALLOW_JAVASCRIPT should be set",
-                sessionRule.session.settings.isAllowJavascript,
+                sessionRule.session.settings.allowJavascript,
                 equalTo(false))
     }
 
     @Test(expected = UiThreadUtils.TimeoutException::class)
     @TimeoutMillis(1000)
     fun noPendingCallbacks() {
         // Make sure we don't have unexpected pending callbacks at the start of a test.
         sessionRule.waitUntilCalled(object : Callbacks.All {})
@@ -888,18 +888,19 @@ class GeckoSessionTestRuleTest : BaseSes
         val newSession = sessionRule.createOpenSession()
         assertThat("Can create session", newSession, notNullValue())
         assertThat("New session is open", newSession.isOpen, equalTo(true))
         assertThat("New session has same settings",
                    newSession.settings, equalTo(sessionRule.session.settings))
     }
 
     @Test fun createOpenSession_withSettings() {
-        val settings = GeckoSessionSettings(sessionRule.session.settings)
-        settings.isUsePrivateMode = true
+        val settings = GeckoSessionSettings.Builder(sessionRule.session.settings)
+                .usePrivateMode(true)
+                .build()
 
         val newSession = sessionRule.createOpenSession(settings)
         assertThat("New session has same settings", newSession.settings, equalTo(settings))
     }
 
     @Test fun createOpenSession_canInterleaveOtherCalls() {
         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
 
@@ -924,18 +925,17 @@ class GeckoSessionTestRuleTest : BaseSes
         val newSession = sessionRule.createClosedSession()
         assertThat("Can create session", newSession, notNullValue())
         assertThat("New session is open", newSession.isOpen, equalTo(false))
         assertThat("New session has same settings",
                    newSession.settings, equalTo(sessionRule.session.settings))
     }
 
     @Test fun createClosedSession_withSettings() {
-        val settings = GeckoSessionSettings(sessionRule.session.settings)
-        settings.isUsePrivateMode = true
+        val settings = GeckoSessionSettings.Builder(sessionRule.session.settings).usePrivateMode(true).build()
 
         val newSession = sessionRule.createClosedSession(settings)
         assertThat("New session has same settings", newSession.settings, equalTo(settings))
     }
 
     @Test(expected = UiThreadUtils.TimeoutException::class)
     @TimeoutMillis(1000)
     @ClosedSessionAtStart
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/NavigationDelegateTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/NavigationDelegateTest.kt
@@ -17,17 +17,16 @@ import org.mozilla.geckoview.test.rule.G
 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.ReuseSession
 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.Setting
 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.WithDevToolsAPI
 import org.mozilla.geckoview.test.util.Callbacks
 
 import android.support.test.filters.MediumTest
 import android.support.test.runner.AndroidJUnit4
 import org.hamcrest.Matchers.*
-import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @RunWith(AndroidJUnit4::class)
 @MediumTest
 @ReuseSession(false)
 class NavigationDelegateTest : BaseSessionTest() {
 
@@ -185,17 +184,17 @@ class NavigationDelegateTest : BaseSessi
                 assertThat("Category should be set",
                            categories,
                            equalTo(category))
                 assertThat("URI should not be null", uri, notNullValue())
                 assertThat("URI should match", uri, endsWith("trackertest.org/tracker.js"))
             }
         })
 
-        sessionRule.session.settings.isUseTrackingProtection = false
+        sessionRule.session.settings.useTrackingProtection = false
 
         sessionRule.session.reload()
         sessionRule.session.waitForPageStop()
 
         sessionRule.forCallbacksDuringWait(
                 object : Callbacks.TrackingProtectionDelegate {
             @AssertCalled(false)
             override fun onTrackerBlocked(session: GeckoSession, uri: String,
@@ -982,23 +981,23 @@ class NavigationDelegateTest : BaseSessi
     @Setting(key = Setting.Key.USE_MULTIPROCESS, value = "false")
     @WithDevToolsAPI
     @Test fun onNewSession_openRemoteFromNonRemote() {
         // Disable popup blocker.
         sessionRule.setPrefsUntilTestEnd(mapOf("dom.disable_open_during_load" to false))
 
         // Ensure a non-remote page can open a remote page, as needed by some tests.
         assertThat("Opening session should be non-remote",
-                   mainSession.settings.isUseMultiprocess,
+                   mainSession.settings.useMultiprocess,
                    equalTo(false))
 
         val newSession = delegateNewSession(
-                GeckoSessionSettings(mainSession.settings).apply {
-                    isUseMultiprocess = true
-                })
+                GeckoSessionSettings.Builder(mainSession.settings)
+                .useMultiprocess(true)
+                .build())
         mainSession.evaluateJS("window.open('http://example.com')")
         newSession.waitForPageStop()
 
         assertThat("window.opener should be set",
                    newSession.evaluateJS("window.opener"), notNullValue())
     }
 
     @WithDevToolsAPI
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/SessionLifecycleTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/SessionLifecycleTest.kt
@@ -74,33 +74,17 @@ class SessionLifecycleTest : BaseSession
     @Test(expected = IllegalStateException::class)
     fun open_throwOnAlreadyOpen() {
         // Throw exception if retrying to open again; otherwise we would leak the old open window.
         sessionRule.session.open()
     }
 
     @Test(expected = IllegalStateException::class)
     fun setChromeURI_throwOnOpenSession() {
-        sessionRule.session.settings.chromeUri = "chrome://invalid/path/to.xul"
-    }
-
-    @Test(expected = IllegalStateException::class)
-    fun setScreenID_throwOnOpenSession() {
-        sessionRule.session.settings.screenId = 42
-    }
-
-    @Test(expected = IllegalStateException::class)
-    fun setUsePrivateMode_throwOnOpenSession() {
-        sessionRule.session.settings.isUsePrivateMode = true
-    }
-
-    @Test(expected = IllegalStateException::class)
-    fun setUseMultiprocess_throwOnOpenSession() {
-        sessionRule.session.settings.isUseMultiprocess =
-                !sessionRule.session.settings.isUseMultiprocess
+        GeckoSessionSettings.Builder(sessionRule.session.settings).chromeUri("chrome://invalid/path/to.xul")
     }
 
     @Test fun readFromParcel() {
         val session = sessionRule.createOpenSession()
 
         session.toParcel { parcel ->
             val newSession = sessionRule.createClosedSession()
             newSession.readFromParcel(parcel)
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/crash/RemoteGeckoService.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/crash/RemoteGeckoService.kt
@@ -26,18 +26,17 @@ class RemoteGeckoService : Service() {
             when (msg.what) {
                 CMD_CRASH_PARENT_NATIVE -> {
                     val settings = GeckoSessionSettings()
                     val session = GeckoSession(settings)
                     session.open(runtime!!)
                     session.loadUri("about:crashparent")
                 }
                 CMD_CRASH_CONTENT_NATIVE -> {
-                    val settings = GeckoSessionSettings()
-                    settings.isUseMultiprocess = true
+                    val settings = GeckoSessionSettings.Builder().useMultiprocess(true).build()
                     val session = GeckoSession(settings)
                     session.open(runtime!!)
                     session.loadUri("about:crashcontent")
                 }
                 else -> {
                     throw RuntimeException("Unhandled command")
                 }
             }
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java
@@ -218,17 +218,17 @@ public class GeckoSessionTestRule implem
                             Method method = GeckoSessionSettings.class.getDeclaredMethod("setBoolean", GeckoSessionSettings.Key.class, boolean.class);
                             method.setAccessible(true);
                             method.invoke(settings, mKey, Boolean.valueOf(value));
                     } else if (int.class.equals(mType) || Integer.class.equals(mType)) {
                         Method method = GeckoSessionSettings.class.getDeclaredMethod("setInt", GeckoSessionSettings.Key.class, int.class);
                         method.setAccessible(true);
                         try {
                             method.invoke(settings, mKey,
-                                    GeckoSessionSettings.class.getField(value)
+                                    (Integer)GeckoSessionSettings.class.getField(value)
                                             .get(null));
                         }
                         catch (final NoSuchFieldException | IllegalAccessException |
                                 ClassCastException e) {
                             method.invoke(settings, mKey,
                                     Integer.valueOf(value));
                         }
                     } else if (String.class.equals(mType)) {
@@ -843,18 +843,17 @@ public class GeckoSessionTestRule implem
     protected boolean mClosedSession;
     protected boolean mWithDevTools;
     protected Map<GeckoSession, Tab> mRDPTabs;
     protected Tab mRDPChromeProcess;
     protected boolean mReuseSession;
     protected boolean mIgnoreCrash;
 
     public GeckoSessionTestRule() {
-        mDefaultSettings = new GeckoSessionSettings();
-        mDefaultSettings.setUseMultiprocess(env.isMultiprocess());
+        mDefaultSettings = new GeckoSessionSettings.Builder().useMultiprocess(env.isMultiprocess()).build();
     }
 
     /**
      * Set an ErrorCollector for assertion errors, or null to not use one.
      *
      * @param ec ErrorCollector or null.
      */
     public void setErrorCollector(final @Nullable ErrorCollector ec) {
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
@@ -7,23 +7,21 @@
 package org.mozilla.geckoview;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
 import java.net.URLConnection;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.List;
 import java.util.UUID;
 
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.GeckoAppShell;
-import org.mozilla.gecko.GeckoEditableChild;
 import org.mozilla.gecko.GeckoThread;
 import org.mozilla.gecko.IGeckoEditableParent;
 import org.mozilla.gecko.mozglue.JNIObject;
 import org.mozilla.gecko.NativeQueue;
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.IntentUtils;
@@ -1243,17 +1241,17 @@ public class GeckoSession implements Par
 
         if (isOpen()) {
             // We will leak the existing Window if we open another one.
             throw new IllegalStateException("Session is open");
         }
 
         final String chromeUri = mSettings.getChromeUri();
         final int screenId =mSettings.getScreenId();
-        final boolean isPrivate = mSettings.isUsePrivateMode();
+        final boolean isPrivate = mSettings.getUsePrivateMode();
 
         mWindow = new Window(runtime, this, mNativeQueue);
 
         onWindowChanged(WINDOW_OPEN, /* inProgress */ true);
 
         if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
             Window.open(mWindow, mNativeQueue, mCompositor, mEventDispatcher,
                         mAccessibility != null ? mAccessibility.nativeProvider : null,
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionSettings.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionSettings.java
@@ -3,26 +3,189 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.geckoview;
 
 import org.mozilla.gecko.util.GeckoBundle;
 
+import android.app.Service;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.util.Log;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.Collection;
 
 public final class GeckoSessionSettings implements Parcelable {
+
+    /**
+     * Settings builder used to construct the settings object.
+     */
+    public static final class Builder {
+        private final GeckoSessionSettings mSettings;
+
+        public Builder() {
+            mSettings = new GeckoSessionSettings();
+        }
+
+        public Builder(final GeckoSessionSettings settings) {
+            mSettings = new GeckoSessionSettings(settings);
+        }
+
+        /**
+         * Finalize and return the settings.
+         *
+         * @return The constructed settings.
+         */
+        public @NonNull GeckoSessionSettings build() {
+            return new GeckoSessionSettings(mSettings);
+        }
+
+        /**
+         * Set the chrome URI.
+         *
+         * @param uri The URI to set the Chrome URI to.
+         * @return This Builder instance.
+
+         */
+        public @NonNull  Builder chromeUri(final String uri) {
+            mSettings.setChromeUri(uri);
+            return this;
+        }
+
+        /**
+         * Set the screen id.
+         *
+         * @param id The screen id.
+         * @return This Builder instance.
+         */
+        public @NonNull Builder screenId(final int id) {
+            mSettings.setScreenId(id);
+            return this;
+        }
+
+        /**
+         * Set the privacy mode for this instance.
+         *
+         * @param flag A flag determining whether Private Mode should be enabled.
+         *             Default is false.
+         * @return This Builder instance.
+         */
+        public @NonNull Builder usePrivateMode(final boolean flag) {
+            mSettings.setUsePrivateMode(flag);
+            return this;
+        }
+
+        /**
+         * Set whether multi-process support should be enabled.
+         *
+         * @param flag A flag determining whether multi-process should be enabled.
+         *             Default is false.
+         * @return This Builder instance.
+         */
+        public @NonNull Builder useMultiprocess(final boolean flag) {
+            mSettings.setUseMultiprocess(flag);
+            return this;
+        }
+
+        /**
+         * Set whether tracking protection should be enabled.
+         *
+         * @param flag A flag determining whether tracking protection should be enabled.
+         *             Default is false.
+         * @return This Builder instance.
+         */
+        public @NonNull Builder useTrackingProtection(final boolean flag) {
+            mSettings.setUseTrackingProtection(flag);
+            return this;
+        }
+
+        /**
+         * Set the user agent mode.
+         *
+         * @param mode The mode to set the user agent to.
+         *             Use one or more of the
+         *             {@link GeckoSessionSettings#USER_AGENT_MODE_MOBILE GeckoSessionSettings.USER_AGENT_MODE_*} flags.
+         * @return This Builder instance.
+         */
+        public @NonNull Builder userAgentMode(final int mode) {
+            mSettings.setUserAgentMode(mode);
+            return this;
+        }
+
+        /**
+         * Override the user agent.
+         *
+         * @param agent The user agent to use.
+         * @return This Builder instance.
+
+         */
+        public @NonNull Builder userAgentOverride(final String agent) {
+            mSettings.setUserAgentOverride(agent);
+            return this;
+        }
+
+        /**
+         * Specify which display-mode to use.
+         *
+         * @param mode The mode to set the display to.
+         *             Use one or more of the
+         *             {@link GeckoSessionSettings#DISPLAY_MODE_BROWSER GeckoSessionSettings.DISPLAY_MODE_*} flags.
+         * @return This Builder instance.
+         */
+        public @NonNull Builder displayMode(final int mode) {
+            mSettings.setDisplayMode(mode);
+            return this;
+        }
+
+        /**
+         * Set whether to suspend the playing of media when the session is inactive.
+         *
+         * @param flag A flag determining whether media should be suspended.
+         *             Default is false.
+         * @return This Builder instance.
+         */
+        public @NonNull Builder suspendMediaWhenInactive(final boolean flag) {
+            mSettings.setSuspendMediaWhenInactive(flag);
+            return this;
+        }
+
+        /**
+         * Set whether JavaScript support should be enabled.
+         *
+         * @param flag A flag determining whether JavaScript should be enabled.
+         *             Default is true.
+         * @return This Builder instance.
+         */
+        public @NonNull Builder allowJavascript(final boolean flag) {
+            mSettings.setAllowJavascript(flag);
+            return this;
+        }
+
+        /**
+         * Set whether the entire accessible tree should be exposed with no caching.
+         *
+         * @param flag A flag determining if the entire accessible tree should be exposed.
+         *             Default is false.
+         * @return This Builder instance.
+         */
+        public @NonNull Builder fullAccessibilityTree(final boolean flag) {
+            mSettings.setFullAccessibilityTree(flag);
+            return this;
+        }
+    }
+
     private static final String LOGTAG = "GeckoSessionSettings";
     private static final boolean DEBUG = false;
 
     // 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;
@@ -149,150 +312,282 @@ public final class GeckoSessionSettings 
         mBundle.putBoolean(SUSPEND_MEDIA_WHEN_INACTIVE.name, false);
         mBundle.putBoolean(ALLOW_JAVASCRIPT.name, true);
         mBundle.putBoolean(FULL_ACCESSIBILITY_TREE.name, false);
         mBundle.putInt(USER_AGENT_MODE.name, USER_AGENT_MODE_MOBILE);
         mBundle.putString(USER_AGENT_OVERRIDE.name, null);
         mBundle.putInt(DISPLAY_MODE.name, DISPLAY_MODE_BROWSER);
     }
 
+
+    /**
+     * Set whether tracking protection should be enabled.
+     *
+     * @param value A flag determining whether tracking protection should be enabled.
+     *             Default is false.
+     */
     public void setUseTrackingProtection(final boolean value) {
         setBoolean(USE_TRACKING_PROTECTION, value);
     }
 
-    public void setUsePrivateMode(final boolean value) {
+    /**
+     * Set the privacy mode for this instance.
+     *
+     * @param value A flag determining whether Private Mode should be enabled.
+     *             Default is false.
+     */
+    private void setUsePrivateMode(final boolean value) {
         setBoolean(USE_PRIVATE_MODE, value);
     }
 
-    public void setUseMultiprocess(final boolean value) {
+
+    /**
+     * Set whether multi-process support should be enabled.
+     *
+     * @param value A flag determining whether multi-process should be enabled.
+     *             Default is false.
+     */
+    private void setUseMultiprocess(final boolean value) {
         setBoolean(USE_MULTIPROCESS, value);
     }
 
+    /**
+     * Set whether to suspend the playing of media when the session is inactive.
+     *
+     * @param value A flag determining whether media should be suspended.
+     *             Default is false.
+     */
     public void setSuspendMediaWhenInactive(final boolean value) {
         setBoolean(SUSPEND_MEDIA_WHEN_INACTIVE, value);
     }
 
+
+    /**
+     * Set whether JavaScript support should be enabled.
+     *
+     * @param value A flag determining whether JavaScript should be enabled.
+     *             Default is true.
+     */
     public void setAllowJavascript(final boolean value) {
         setBoolean(ALLOW_JAVASCRIPT, value);
     }
 
+
+    /**
+     * Set whether the entire accessible tree should be exposed with no caching.
+     *
+     * @param value A flag determining full accessibility tree should be exposed.
+     *             Default is false.
+     */
     public void setFullAccessibilityTree(final boolean value) {
         setBoolean(FULL_ACCESSIBILITY_TREE, value);
     }
 
     private void setBoolean(final Key<Boolean> key, final boolean value) {
         synchronized (mBundle) {
             if (valueChangedLocked(key, value)) {
                 mBundle.putBoolean(key.name, value);
                 dispatchUpdate();
             }
         }
     }
 
-    public boolean isUseTrackingProtection() {
+    /**
+     * Whether tracking protection is enabled.
+     *
+     * @return true if tracking protection is enabled, false if not.
+     */
+    public boolean getUseTrackingProtection() {
         return getBoolean(USE_TRACKING_PROTECTION);
     }
 
-    public boolean isUsePrivateMode() {
+    /**
+     * Whether private mode is enabled.
+     *
+     * @return true if private mode is enabled, false if not.
+     */
+    public boolean getUsePrivateMode() {
         return getBoolean(USE_PRIVATE_MODE);
     }
 
-    public boolean isUseMultiprocess() {
+    /**
+     * Whether multiprocess is enabled.
+     *
+     * @return true if multiprocess is enabled, false if not.
+     */
+    public boolean getUseMultiprocess() {
         return getBoolean(USE_MULTIPROCESS);
     }
 
-    public boolean isSuspendMediaWhenInactive() {
+    /**
+     * Whether media will be suspended when the session is inactice.
+     *
+     * @return true if media will be suspended, false if not.
+     */
+    public boolean getSuspendMediaWhenInactive() {
         return getBoolean(SUSPEND_MEDIA_WHEN_INACTIVE);
     }
 
-    public boolean isAllowJavascript() {
+    /**
+     * Whether javascript execution is allowed.
+     *
+     * @return true if javascript execution is allowed, false if not.
+     */
+    public boolean getAllowJavascript() {
         return getBoolean(ALLOW_JAVASCRIPT);
     }
 
-    public boolean isFullAccessibilityTree() {
+    /**
+     * Whether entire accessible tree is exposed with no caching.
+     *
+     * @return true if accessibility tree is exposed, false if not.
+     */
+    public boolean getFullAccessibilityTree() {
         return getBoolean(FULL_ACCESSIBILITY_TREE);
     }
 
     private boolean getBoolean(final Key<Boolean> key) {
         synchronized (mBundle) {
             return mBundle.getBoolean(key.name);
         }
     }
 
-    public void setScreenId(final int value) {
+
+    /**
+     * Set the screen id.
+     *
+     * @param value The screen id.
+     */
+    private void setScreenId(final int value) {
         setInt(SCREEN_ID, value);
     }
 
+
+    /**
+     * Specify which user agent mode we should use
+     *
+     * @param value One or more of the
+     *             {@link GeckoSessionSettings#USER_AGENT_MODE_MOBILE GeckoSessionSettings.USER_AGENT_MODE_*} flags.
+     */
     public void setUserAgentMode(final int value) {
         setInt(USER_AGENT_MODE, value);
     }
 
+
+    /**
+     * Set the display mode.
+     *
+     * @param value The mode to set the display to.
+     *             Use one or more of the
+     *             {@link GeckoSessionSettings#DISPLAY_MODE_BROWSER GeckoSessionSettings.DISPLAY_MODE_*} flags.
+     */
     public void setDisplayMode(final int value) {
         setInt(DISPLAY_MODE, value);
     }
 
     private void setInt(final Key<Integer> key, final int value) {
         synchronized (mBundle) {
             if (valueChangedLocked(key, value)) {
                 mBundle.putInt(key.name, value);
                 dispatchUpdate();
             }
         }
     }
 
+    /**
+     * Set the window screen ID.
+     * Read-only once session is open.
+     * Use the {@link Builder} to set on session open.
+     *
+     * @return Key to set the window screen ID. 0 is the default ID.
+     */
     public int getScreenId() {
         return getInt(SCREEN_ID);
     }
 
+    /**
+     * The current user agent Mode
+     * @return One or more of the
+     *      {@link GeckoSessionSettings#USER_AGENT_MODE_MOBILE GeckoSessionSettings.USER_AGENT_MODE_*} flags.
+     */
     public int getUserAgentMode() {
         return getInt(USER_AGENT_MODE);
     }
 
+    /**
+     * The current display mode.
+     * @return )One or more of the
+     *      {@link GeckoSessionSettings#DISPLAY_MODE_BROWSER GeckoSessionSettings.DISPLAY_MODE_*} flags.
+     */
     public int getDisplayMode() {
         return getInt(DISPLAY_MODE);
     }
 
     private int getInt(final Key<Integer> key) {
         synchronized (mBundle) {
             return mBundle.getInt(key.name);
         }
     }
 
-    public void setChromeUri(final String value) {
+    /**
+     * Set the chrome URI.
+     *
+     * @param value The URI to set the Chrome URI to.
+
+     */
+    private void setChromeUri(final String value) {
         setString(CHROME_URI, value);
     }
 
+
+    /**
+     * Specify the user agent override string.
+     * Set value to null to use the user agent specified by USER_AGENT_MODE.
+     * @param value
+     */
     public void setUserAgentOverride(final String value) {
         setString(USER_AGENT_OVERRIDE, value);
     }
 
     private void setString(final Key<String> key, final String value) {
         synchronized (mBundle) {
             if (valueChangedLocked(key, value)) {
                 mBundle.putString(key.name, value);
                 dispatchUpdate();
             }
         }
     }
 
+    /**
+     * Set the chrome window URI.
+     * Read-only once session is open.
+     * Use the {@link Builder} to set on session open.
+     *
+     * @return Key to set the chrome window URI, or null to use default URI.
+     */
     public String getChromeUri() {
         return getString(USER_AGENT_OVERRIDE);
     }
 
+    /**
+     * The user agent override string.
+     * @return The current user agent string or null if the agent is specified by
+     *          {@link GeckoSessionSettings#USER_AGENT_MODE}
+     */
     public String getUserAgentOverride() {
         return getString(USER_AGENT_OVERRIDE);
     }
 
     private String getString(final Key<String> key) {
         synchronized (mBundle) {
             return mBundle.getString(key.name);
         }
     }
 
-    /* package */ GeckoBundle toBundle() {
+    /* package */ @NonNull GeckoBundle toBundle() {
         return new GeckoBundle(mBundle);
     }
 
     @Override
     public String toString() {
         return mBundle.toString();
     }
 
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionAccessibility.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionAccessibility.java
@@ -108,17 +108,17 @@ public class SessionAccessibility {
         return "android.view.View"; // Fallback class is View
     }
 
     /* package */ final class NodeProvider extends AccessibilityNodeProvider {
         @Override
         public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualDescendantId) {
             AccessibilityNodeInfo node = null;
             if (mAttached) {
-                node = mSession.getSettings().isFullAccessibilityTree() ?
+                node = mSession.getSettings().getFullAccessibilityTree() ?
                         getNodeFromGecko(virtualDescendantId) : getNodeFromCache(virtualDescendantId);
                 if (node != null) {
                     node.setAccessibilityFocused(mAccessibilityFocusedNode == virtualDescendantId);
                     node.setFocused(mFocusedNode == virtualDescendantId);
                 }
             }
 
             if (node == null) {
@@ -649,17 +649,17 @@ public class SessionAccessibility {
         if (!Settings.isPlatformEnabled() && (Build.VERSION.SDK_INT < 17 || mView.getDisplay() != null)) {
             // Accessibility could be activated in Gecko via xpcom, for example when using a11y
             // devtools. Here we assure that either Android a11y is *really* enabled, or no
             // display is attached and we must be in a junit test.
             return;
         }
 
         GeckoBundle cachedBundle = null;
-        if (!mSession.getSettings().isFullAccessibilityTree()) {
+        if (!mSession.getSettings().getFullAccessibilityTree()) {
             cachedBundle = getMostRecentBundle(sourceId);
             // Suppress events from non cached nodes if cache is enabled.
             if (cachedBundle == null && sourceId != View.NO_ID) {
                 return;
             }
         }
 
         final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);