Bug 1500155 - Part 1 - Add methods for each setting in `GeckoSessionSettings`. r=geckoview-reviewers,snorp,esawin
☠☠ backed out by 66e1668bb44c ☠ ☠
authorEmily Toop <etoop@mozilla.com>
Wed, 28 Nov 2018 13:57:02 +0000
changeset 504955 84081d5487039fdb4aeca8245ed58bfa34d5051b
parent 504927 33a1a7271fe37f0e5118dc71381f7e29d3809a89
child 504956 f7f8b232d111ba86b0ac3d9c7aa65a7be21f82ea
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgeckoview-reviewers, snorp, esawin
bugs1500155
milestone65.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 1 - Add methods for each setting in `GeckoSessionSettings`. r=geckoview-reviewers,snorp,esawin Part 2 - Make old methods and fields for get/set<DataType> in `GeckoSessionSettings` private. Migrate existing code to use these new methods instead of the exisiting get/set<DataType>(Key, Value) methods. This commit does not tackle making the old functions inaccessible publicly as this requires a complete refactoring of the Setting @interface in `GeckoSessionTestRule.java`. There may be a follow up commit if this is something we want to do. Make `GeckoSessionSettings` fields and get<Type> methods private. Update `GeckoSessionTestRule` to access private fields and methods using reflection. Differential Revision: https://phabricator.services.mozilla.com/D12435
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/CHANGELOG.md
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/CHANGELOG.md
+++ b/mobile/android/geckoview/CHANGELOG.md
@@ -19,10 +19,11 @@
 - Changed `LoadRequest.isUserTriggered` to `isRedirect`.
 - Added `GeckoSession.LOAD_FLAGS_BYPASS_CLASSIFIER` to bypass the URI
   classifier.
 - Added a `protected` empty constructor to all field-only classes so that apps
   can mock these classes in tests.
 - Added `ContentDelegate.ContextElement` to extend the information passed to
   `ContentDelegate#onContextMenu`. Extended information includes the element's
   title and alt attributes.
+ - Add methods for each setting in GeckoSessionSettings
 
-[api-version]: 8cfd04a09e7a242b3da22ccdd55c88a2aca2ba6d
+[api-version]: 7b2d6a4ff4cef4099607af2070005ecd360c5311
--- a/mobile/android/geckoview/api.txt
+++ b/mobile/android/geckoview/api.txt
@@ -657,45 +657,63 @@ 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 getBoolean(org.mozilla.geckoview.GeckoSessionSettings.Key<java.lang.Boolean>);
-    method public int getInt(org.mozilla.geckoview.GeckoSessionSettings.Key<java.lang.Integer>);
-    method public java.lang.String getString(org.mozilla.geckoview.GeckoSessionSettings.Key<java.lang.String>);
+    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 void readFromParcel(android.os.Parcel);
-    method public void setBoolean(org.mozilla.geckoview.GeckoSessionSettings.Key<java.lang.Boolean>, boolean);
-    method public void setInt(org.mozilla.geckoview.GeckoSessionSettings.Key<java.lang.Integer>, int);
-    method public void setString(org.mozilla.geckoview.GeckoSessionSettings.Key<java.lang.String>, java.lang.String);
+    method public void setAllowJavascript(boolean);
+    method public void setDisplayMode(int);
+    method public void setFullAccessibilityTree(boolean);
+    method public void setSuspendMediaWhenInactive(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 org.mozilla.geckoview.GeckoSessionSettings.Key<java.lang.Boolean> ALLOW_JAVASCRIPT;
-    field public static final org.mozilla.geckoview.GeckoSessionSettings.Key<java.lang.String> CHROME_URI;
     field public static final android.os.Parcelable.Creator<org.mozilla.geckoview.GeckoSessionSettings> CREATOR;
-    field public static final org.mozilla.geckoview.GeckoSessionSettings.Key<java.lang.Integer> DISPLAY_MODE;
     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 org.mozilla.geckoview.GeckoSessionSettings.Key<java.lang.Boolean> FULL_ACCESSIBILITY_TREE;
-    field public static final org.mozilla.geckoview.GeckoSessionSettings.Key<java.lang.Integer> SCREEN_ID;
-    field public static final org.mozilla.geckoview.GeckoSessionSettings.Key<java.lang.Boolean> SUSPEND_MEDIA_WHEN_INACTIVE;
-    field public static final org.mozilla.geckoview.GeckoSessionSettings.Key<java.lang.Integer> USER_AGENT_MODE;
     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;
-    field public static final org.mozilla.geckoview.GeckoSessionSettings.Key<java.lang.String> USER_AGENT_OVERRIDE;
-    field public static final org.mozilla.geckoview.GeckoSessionSettings.Key<java.lang.Boolean> USE_MULTIPROCESS;
-    field public static final org.mozilla.geckoview.GeckoSessionSettings.Key<java.lang.Boolean> USE_PRIVATE_MODE;
-    field public static final org.mozilla.geckoview.GeckoSessionSettings.Key<java.lang.Boolean> USE_TRACKING_PROTECTION;
+  }
+
+  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);
--- 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.getBoolean(GeckoSessionSettings.USE_MULTIPROCESS)
+        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,29 +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.getBoolean(
-                           GeckoSessionSettings.USE_PRIVATE_MODE),
+                   sessionRule.session.settings.usePrivateMode,
                    equalTo(true))
         assertThat("DISPLAY_MODE should be set",
-                   sessionRule.session.settings.getInt(GeckoSessionSettings.DISPLAY_MODE),
+                   sessionRule.session.settings.displayMode,
                    equalTo(GeckoSessionSettings.DISPLAY_MODE_MINIMAL_UI))
         assertThat("USE_TRACKING_PROTECTION should be set",
-                   sessionRule.session.settings.getBoolean(
-                           GeckoSessionSettings.USE_TRACKING_PROTECTION),
+                   sessionRule.session.settings.useTrackingProtection,
                    equalTo(true))
         assertThat("ALLOW_JAVASCRIPT should be set",
-                sessionRule.session.settings.getBoolean(
-                        GeckoSessionSettings.ALLOW_JAVASCRIPT),
+                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 {})
@@ -891,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.setBoolean(GeckoSessionSettings.USE_PRIVATE_MODE, 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)
 
@@ -927,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.setBoolean(GeckoSessionSettings.USE_PRIVATE_MODE, 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
@@ -2,32 +2,31 @@
  * Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 package org.mozilla.geckoview.test
 
 import org.mozilla.geckoview.AllowOrDeny
 import org.mozilla.geckoview.GeckoResult
 import org.mozilla.geckoview.GeckoSession
-import org.mozilla.geckoview.GeckoSession.NavigationDelegate.LoadRequest;
-import org.mozilla.geckoview.GeckoSession.TrackingProtectionDelegate;
+import org.mozilla.geckoview.GeckoSession.NavigationDelegate.LoadRequest
+import org.mozilla.geckoview.GeckoSession.TrackingProtectionDelegate
 import org.mozilla.geckoview.GeckoSessionSettings
 import org.mozilla.geckoview.WebRequestError
 
 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.AssertCalled
 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.NullDelegate
 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() {
 
@@ -60,17 +59,17 @@ class NavigationDelegateTest : BaseSessi
                     }
 
                     @AssertCalled(count = 1, order = [4])
                     override fun onPageStop(session: GeckoSession, success: Boolean) {
                         assertThat("Load should fail", success, equalTo(false))
                     }
                 })
 
-        sessionRule.session.loadUri(testUri);
+        sessionRule.session.loadUri(testUri)
         sessionRule.waitForPageStop()
 
         if (errorPageUrl != null) {
             sessionRule.waitUntilCalled(object : Callbacks.ContentDelegate, Callbacks.NavigationDelegate {
                 @AssertCalled(count = 1, order = [1])
                 override fun onLocationChange(session: GeckoSession, url: String) {
                     assertThat("URL should match", url, equalTo(testUri))
                 }
@@ -119,17 +118,17 @@ class NavigationDelegateTest : BaseSessi
 
         sessionRule.session.loadUri(testUri)
         sessionRule.waitUntilCalled(Callbacks.NavigationDelegate::class, "onLoadError")
 
         if (errorPageUrl != null) {
             sessionRule.waitUntilCalled(object: Callbacks.ContentDelegate {
                 @AssertCalled(count = 1)
                 override fun onTitleChange(session: GeckoSession, title: String) {
-                    assertThat("Title should not be empty", title, not(isEmptyOrNullString()));
+                    assertThat("Title should not be empty", title, not(isEmptyOrNullString()))
                 }
             })
         }
     }
 
     fun testLoadEarlyError(testUri: String, expectedCategory: Int,
                            expectedError: Int) {
         testLoadEarlyErrorWithErrorPage(testUri, expectedCategory, expectedError, createTestUrl(HELLO_HTML_PATH))
@@ -163,40 +162,39 @@ class NavigationDelegateTest : BaseSessi
     @Test fun loadUntrusted() {
         val uri = if (sessionRule.env.isAutomation) {
             "https://expired.example.com/"
         } else {
             "https://expired.badssl.com/"
         }
         testLoadExpectError(uri,
                 WebRequestError.ERROR_CATEGORY_SECURITY,
-                WebRequestError.ERROR_SECURITY_BAD_CERT);
+                WebRequestError.ERROR_SECURITY_BAD_CERT)
     }
 
     @Setting(key = Setting.Key.USE_TRACKING_PROTECTION, value = "true")
     @Test fun trackingProtection() {
-        val category = TrackingProtectionDelegate.CATEGORY_TEST;
+        val category = TrackingProtectionDelegate.CATEGORY_TEST
         sessionRule.runtime.settings.trackingProtectionCategories = category
         sessionRule.session.loadTestPath(TRACKERS_PATH)
 
         sessionRule.waitUntilCalled(
                 object : Callbacks.TrackingProtectionDelegate {
             @AssertCalled(count = 1)
             override fun onTrackerBlocked(session: GeckoSession, uri: String,
                                           categories: Int) {
                 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.setBoolean(
-            GeckoSessionSettings.USE_TRACKING_PROTECTION, 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,
@@ -365,60 +363,57 @@ class NavigationDelegateTest : BaseSessi
         val userAgentJs = "window.navigator.userAgent"
         val mobileSubStr = "Mobile"
         val desktopSubStr = "X11"
 
         assertThat("User agent should be set to mobile",
                    sessionRule.session.evaluateJS(userAgentJs) as String,
                    containsString(mobileSubStr))
 
-        var userAgent = sessionRule.waitForResult(sessionRule.session.getUserAgent());
+        var userAgent = sessionRule.waitForResult(sessionRule.session.userAgent)
         assertThat("User agent should be reported as mobile",
-                    userAgent, containsString(mobileSubStr));
+                    userAgent, containsString(mobileSubStr))
 
-        sessionRule.session.settings.setInt(
-            GeckoSessionSettings.USER_AGENT_MODE, GeckoSessionSettings.USER_AGENT_MODE_DESKTOP)
+        sessionRule.session.settings.userAgentMode = GeckoSessionSettings.USER_AGENT_MODE_DESKTOP
 
         sessionRule.session.reload()
         sessionRule.session.waitForPageStop()
 
         assertThat("User agent should be set to desktop",
                    sessionRule.session.evaluateJS(userAgentJs) as String,
                    containsString(desktopSubStr))
 
-        userAgent = sessionRule.waitForResult(sessionRule.session.getUserAgent());
+        userAgent = sessionRule.waitForResult(sessionRule.session.userAgent)
         assertThat("User agent should be reported as desktop",
-                    userAgent, containsString(desktopSubStr));
+                    userAgent, containsString(desktopSubStr))
 
-        sessionRule.session.settings.setInt(
-            GeckoSessionSettings.USER_AGENT_MODE, GeckoSessionSettings.USER_AGENT_MODE_MOBILE)
+        sessionRule.session.settings.userAgentMode = GeckoSessionSettings.USER_AGENT_MODE_MOBILE
 
         sessionRule.session.reload()
         sessionRule.session.waitForPageStop()
 
         assertThat("User agent should be set to mobile",
                    sessionRule.session.evaluateJS(userAgentJs) as String,
                    containsString(mobileSubStr))
 
-        userAgent = sessionRule.waitForResult(sessionRule.session.getUserAgent());
+        userAgent = sessionRule.waitForResult(sessionRule.session.userAgent)
         assertThat("User agent should be reported as mobile",
-                    userAgent, containsString(mobileSubStr));
+                    userAgent, containsString(mobileSubStr))
 
         val vrSubStr = "Mobile VR"
-        sessionRule.session.settings.setInt(
-                GeckoSessionSettings.USER_AGENT_MODE, GeckoSessionSettings.USER_AGENT_MODE_VR)
+        sessionRule.session.settings.userAgentMode = GeckoSessionSettings.USER_AGENT_MODE_VR
 
         sessionRule.session.reload()
         sessionRule.session.waitForPageStop()
 
         assertThat("User agent should be set to VR",
                 sessionRule.session.evaluateJS(userAgentJs) as String,
                 containsString(vrSubStr))
 
-        userAgent = sessionRule.waitForResult(sessionRule.session.getUserAgent())
+        userAgent = sessionRule.waitForResult(sessionRule.session.userAgent)
         assertThat("User agent should be reported as VR",
                 userAgent, containsString(vrSubStr))
 
     }
 
     @WithDevToolsAPI
     @Test fun uaOverride() {
         sessionRule.session.loadUri("https://example.com")
@@ -428,62 +423,61 @@ class NavigationDelegateTest : BaseSessi
         val mobileSubStr = "Mobile"
         val vrSubStr = "Mobile VR"
         val overrideUserAgent = "This is the override user agent"
 
         var userAgent = sessionRule.session.evaluateJS(userAgentJs) as String
         assertThat("User agent should be reported as mobile",
                 userAgent, containsString(mobileSubStr))
 
-        sessionRule.session.settings.setString(GeckoSessionSettings.USER_AGENT_OVERRIDE, overrideUserAgent)
+        sessionRule.session.settings.userAgentOverride = overrideUserAgent
 
         sessionRule.session.reload()
         sessionRule.session.waitForPageStop()
 
         userAgent = sessionRule.session.evaluateJS(userAgentJs) as String
 
         assertThat("User agent should be reported as override",
                 userAgent, equalTo(overrideUserAgent))
 
-        sessionRule.session.settings.setInt(
-                GeckoSessionSettings.USER_AGENT_MODE, GeckoSessionSettings.USER_AGENT_MODE_VR)
+        sessionRule.session.settings.userAgentMode = GeckoSessionSettings.USER_AGENT_MODE_VR
 
         sessionRule.session.reload()
         sessionRule.session.waitForPageStop()
 
         assertThat("User agent should still be reported as override even when USER_AGENT_MODE is set",
                 userAgent, equalTo(overrideUserAgent))
 
-        sessionRule.session.settings.setString(GeckoSessionSettings.USER_AGENT_OVERRIDE, null)
+        sessionRule.session.settings.userAgentOverride = null
 
         sessionRule.session.reload()
         sessionRule.session.waitForPageStop()
 
         userAgent = sessionRule.session.evaluateJS(userAgentJs) as String
         assertThat("User agent should now be reported as VR",
                 userAgent, containsString(vrSubStr))
 
         sessionRule.delegateDuringNextWait(object : Callbacks.NavigationDelegate {
             override fun onLoadRequest(session: GeckoSession, request: LoadRequest): GeckoResult<AllowOrDeny>? {
-                sessionRule.session.settings.setString(GeckoSessionSettings.USER_AGENT_OVERRIDE, overrideUserAgent)
+                sessionRule.session.settings.userAgentOverride = overrideUserAgent
                 return null
             }
         })
 
         sessionRule.session.reload()
         sessionRule.session.waitForPageStop()
 
         userAgent = sessionRule.session.evaluateJS(userAgentJs) as String
 
         assertThat("User agent should be reported as override after being set in onLoadRequest",
                 userAgent, equalTo(overrideUserAgent))
 
         sessionRule.delegateDuringNextWait(object : Callbacks.NavigationDelegate {
             override fun onLoadRequest(session: GeckoSession, request: LoadRequest): GeckoResult<AllowOrDeny>? {
-                sessionRule.session.settings.setString(GeckoSessionSettings.USER_AGENT_OVERRIDE, null)
+                sessionRule.session.settings.userAgentOverride = null
                 return null
             }
         })
 
         sessionRule.session.reload()
         sessionRule.session.waitForPageStop()
 
         userAgent = sessionRule.session.evaluateJS(userAgentJs) as String
@@ -551,18 +545,18 @@ class NavigationDelegateTest : BaseSessi
             override fun onNewSession(session: GeckoSession, uri: String): GeckoResult<GeckoSession>? {
                 return null
             }
         })
     }
 
     @Test fun load_dataUri() {
         val dataUrl = "data:,Hello%2C%20World!"
-        sessionRule.session.loadUri(dataUrl);
-        sessionRule.waitForPageStop();
+        sessionRule.session.loadUri(dataUrl)
+        sessionRule.waitForPageStop()
 
         sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate, Callbacks.ProgressDelegate {
             @AssertCalled(count = 1)
             override fun onLocationChange(session: GeckoSession, url: String) {
                 assertThat("URL should match the provided data URL", url, equalTo(dataUrl))
             }
 
             @AssertCalled(count = 1)
@@ -604,22 +598,22 @@ class NavigationDelegateTest : BaseSessi
         assertThat("Should not get callback for second load",
                    onLocationCount, equalTo(1))
     }
 
     @Test fun loadString() {
         val dataString = "<html><head><title>TheTitle</title></head><body>TheBody</body></html>"
         val mimeType = "text/html"
         sessionRule.session.loadString(dataString, mimeType)
-        sessionRule.waitForPageStop();
+        sessionRule.waitForPageStop()
 
         sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate, Callbacks.ProgressDelegate, Callbacks.ContentDelegate {
             @AssertCalled
             override fun onTitleChange(session: GeckoSession, title: String) {
-                assertThat("Title should match", title, equalTo("TheTitle"));
+                assertThat("Title should match", title, equalTo("TheTitle"))
             }
 
             @AssertCalled(count = 1)
             override fun onLocationChange(session: GeckoSession, url: String) {
                 assertThat("URL should be a data URL", url,
                            equalTo(GeckoSession.createDataUri(dataString, mimeType)))
             }
 
@@ -627,17 +621,17 @@ class NavigationDelegateTest : BaseSessi
             override fun onPageStop(session: GeckoSession, success: Boolean) {
                 assertThat("Page should load successfully", success, equalTo(true))
             }
         })
     }
 
     @Test fun loadString_noMimeType() {
         sessionRule.session.loadString("Hello, World!", null)
-        sessionRule.waitForPageStop();
+        sessionRule.waitForPageStop()
 
         sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate, Callbacks.ProgressDelegate {
             @AssertCalled(count = 1)
             override fun onLocationChange(session: GeckoSession, url: String) {
                 assertThat("URL should be a data URL", url, startsWith("data:"))
             }
 
             @AssertCalled(count = 1)
@@ -646,18 +640,18 @@ class NavigationDelegateTest : BaseSessi
             }
         })
     }
 
     @Test fun loadData_html() {
         val bytes = getTestBytes(HELLO_HTML_PATH)
         assertThat("test html should have data", bytes.size, greaterThan(0))
 
-        sessionRule.session.loadData(bytes, "text/html");
-        sessionRule.waitForPageStop();
+        sessionRule.session.loadData(bytes, "text/html")
+        sessionRule.waitForPageStop()
 
         sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate, Callbacks.ProgressDelegate, Callbacks.ContentDelegate {
             @AssertCalled(count = 1)
             override fun onTitleChange(session: GeckoSession, title: String) {
                 assertThat("Title should match", title, equalTo("Hello, world!"))
             }
 
             @AssertCalled(count = 1)
@@ -671,18 +665,18 @@ class NavigationDelegateTest : BaseSessi
             }
         })
     }
 
     fun loadDataHelper(assetPath: String, mimeType: String? = null) {
         val bytes = getTestBytes(assetPath)
         assertThat("test data should have bytes", bytes.size, greaterThan(0))
 
-        sessionRule.session.loadData(bytes, mimeType);
-        sessionRule.waitForPageStop();
+        sessionRule.session.loadData(bytes, mimeType)
+        sessionRule.waitForPageStop()
 
         sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate, Callbacks.ProgressDelegate {
             @AssertCalled(count = 1)
             override fun onLocationChange(session: GeckoSession, url: String) {
                 assertThat("URL should match", url, equalTo(GeckoSession.createDataUri(bytes, mimeType)))
             }
 
             @AssertCalled(count = 1)
@@ -987,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.getBoolean(GeckoSessionSettings.USE_MULTIPROCESS),
+                   mainSession.settings.useMultiprocess,
                    equalTo(false))
 
         val newSession = delegateNewSession(
-                GeckoSessionSettings(mainSession.settings).apply {
-                    setBoolean(GeckoSessionSettings.USE_MULTIPROCESS, 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
@@ -72,38 +72,16 @@ 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.setString(GeckoSessionSettings.CHROME_URI, "chrome://invalid/path/to.xul")
-    }
-
-    @Test(expected = IllegalStateException::class)
-    fun setScreenID_throwOnOpenSession() {
-        sessionRule.session.settings.setInt(GeckoSessionSettings.SCREEN_ID, 42)
-    }
-
-    @Test(expected = IllegalStateException::class)
-    fun setUsePrivateMode_throwOnOpenSession() {
-        sessionRule.session.settings.setBoolean(GeckoSessionSettings.USE_PRIVATE_MODE, true)
-    }
-
-    @Test(expected = IllegalStateException::class)
-    fun setUseMultiprocess_throwOnOpenSession() {
-        sessionRule.session.settings.setBoolean(
-                GeckoSessionSettings.USE_MULTIPROCESS,
-                !sessionRule.session.settings.getBoolean(GeckoSessionSettings.USE_MULTIPROCESS))
-    }
-
     @Test fun readFromParcel() {
         val session = sessionRule.createOpenSession()
 
         session.toParcel { parcel ->
             val newSession = sessionRule.createClosedSession()
             newSession.readFromParcel(parcel)
 
             assertThat("New session has same settings",
--- 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,19 @@ 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.setBoolean(GeckoSessionSettings.USE_MULTIPROCESS, 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
@@ -195,46 +195,68 @@ public class GeckoSessionTestRule implem
             FULL_ACCESSIBILITY_TREE;
 
             private final GeckoSessionSettings.Key<?> mKey;
             private final Class<?> mType;
 
             Key() {
                 final Field field;
                 try {
-                    field = GeckoSessionSettings.class.getField(name());
+                    field = GeckoSessionSettings.class.getDeclaredField(name());
+                    field.setAccessible(true);
                     mKey = (GeckoSessionSettings.Key<?>) field.get(null);
                 } catch (final NoSuchFieldException | IllegalAccessException e) {
                     throw new RuntimeException(e);
                 }
 
                 final ParameterizedType genericType = (ParameterizedType) field.getGenericType();
                 mType = (Class<?>) genericType.getActualTypeArguments()[0];
             }
 
             @SuppressWarnings("unchecked")
             public void set(final GeckoSessionSettings settings, final String value) {
-                if (boolean.class.equals(mType) || Boolean.class.equals(mType)) {
-                    settings.setBoolean((GeckoSessionSettings.Key<Boolean>) mKey,
-                            Boolean.valueOf(value));
-                } else if (int.class.equals(mType) || Integer.class.equals(mType)) {
-                    try {
-                        settings.setInt((GeckoSessionSettings.Key<Integer>) mKey,
-                                (Integer) GeckoSessionSettings.class.getField(value)
-                                        .get(null));
-                    } catch (final NoSuchFieldException | IllegalAccessException |
-                            ClassCastException e) {
-                        settings.setInt((GeckoSessionSettings.Key<Integer>) mKey,
-                                        Integer.valueOf(value));
+                try {
+                    if (boolean.class.equals(mType) || Boolean.class.equals(mType)) {
+                        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,
+                                    (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)) {
+                        Method method = GeckoSessionSettings.class
+                                .getDeclaredMethod("setString",
+                                        GeckoSessionSettings.Key.class,
+                                        String.class);
+                        method.setAccessible(true);
+                        method.invoke(settings, mKey, value);
+                    } else {
+                        throw new IllegalArgumentException("Unsupported type: " +
+                                mType.getSimpleName());
                     }
-                } else if (String.class.equals(mType)) {
-                    settings.setString((GeckoSessionSettings.Key<String>) mKey, value);
-                } else {
-                    throw new IllegalArgumentException("Unsupported type: " +
-                            mType.getSimpleName());
+                } catch (NoSuchMethodException
+                        | IllegalAccessException
+                        | InvocationTargetException e) {
+                    throw new RuntimeException(e);
                 }
             }
         }
 
         @Target({ElementType.METHOD, ElementType.TYPE})
         @Retention(RetentionPolicy.RUNTIME)
         @interface List {
             Setting[] value();
@@ -832,18 +854,19 @@ 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.setBoolean(GeckoSessionSettings.USE_MULTIPROCESS, 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;
@@ -1241,19 +1239,19 @@ public class GeckoSession implements Par
     public void open(final @NonNull GeckoRuntime runtime) {
         ThreadUtils.assertOnUiThread();
 
         if (isOpen()) {
             // We will leak the existing Window if we open another one.
             throw new IllegalStateException("Session is open");
         }
 
-        final String chromeUri = mSettings.getString(GeckoSessionSettings.CHROME_URI);
-        final int screenId = mSettings.getInt(GeckoSessionSettings.SCREEN_ID);
-        final boolean isPrivate = mSettings.getBoolean(GeckoSessionSettings.USE_PRIVATE_MODE);
+        final String chromeUri = mSettings.getChromeUri();
+        final int screenId = mSettings.getScreenId();
+        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;
@@ -48,81 +211,81 @@ public final class GeckoSessionSettings 
             this.values = values;
         }
     }
 
     /**
      * 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 =
+    private 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 =
+    private static final Key<Integer> SCREEN_ID =
         new Key<Integer>("screenId", /* initOnly */ true, /* values */ null);
 
     /**
      * Key to enable and disable tracking protection.
      */
-    public static final Key<Boolean> USE_TRACKING_PROTECTION =
+    private static final Key<Boolean> USE_TRACKING_PROTECTION =
         new Key<Boolean>("useTrackingProtection");
     /**
      * Key to enable and disable private mode browsing.
      * Read-only once session is open.
      */
-    public static final Key<Boolean> USE_PRIVATE_MODE =
+    private static final Key<Boolean> USE_PRIVATE_MODE =
         new Key<Boolean>("usePrivateMode", /* initOnly */ true, /* values */ null);
 
     /**
      * Key to enable and disable multiprocess browsing (e10s).
      * Read-only once session is open.
      */
-    public static final Key<Boolean> USE_MULTIPROCESS =
+    private static final Key<Boolean> USE_MULTIPROCESS =
         new Key<Boolean>("useMultiprocess", /* initOnly */ true, /* values */ null);
 
     /**
      * Key to specify which user agent mode we should use.
      */
-    public static final Key<Integer> USER_AGENT_MODE =
+    private static final Key<Integer> USER_AGENT_MODE =
         new Key<Integer>("userAgentMode", /* initOnly */ false,
                          Arrays.asList(USER_AGENT_MODE_MOBILE, USER_AGENT_MODE_DESKTOP, USER_AGENT_MODE_VR));
 
     /**
      * Key to specify the user agent override string.
      * Set value to null to use the user agent specified by USER_AGENT_MODE.
      */
-    public static final Key<String> USER_AGENT_OVERRIDE =
+    private static final Key<String> USER_AGENT_OVERRIDE =
         new Key<String>("userAgentOverride", /* initOnly */ false, /* values */ null);
 
     /**
      * Key to specify which display-mode we should use.
      */
-    public static final Key<Integer> DISPLAY_MODE =
+    private static final Key<Integer> DISPLAY_MODE =
         new Key<Integer>("displayMode", /* initOnly */ false,
                          Arrays.asList(DISPLAY_MODE_BROWSER, DISPLAY_MODE_MINIMAL_UI,
                                        DISPLAY_MODE_STANDALONE, DISPLAY_MODE_FULLSCREEN));
 
     /**
      * Key to specify if media should be suspended when the session is inactive.
      */
-    public static final Key<Boolean> SUSPEND_MEDIA_WHEN_INACTIVE =
+    private static final Key<Boolean> SUSPEND_MEDIA_WHEN_INACTIVE =
         new Key<Boolean>("suspendMediaWhenInactive", /* initOnly */ false, /* values */ null);
 
     /**
      * Key to specify if JavaScript should be allowed on this session.
      */
-    public static final Key<Boolean> ALLOW_JAVASCRIPT =
+    private static final Key<Boolean> ALLOW_JAVASCRIPT =
             new Key<Boolean>("allowJavascript", /* initOnly */ false, /* values */ null);
     /**
      * Key to specify if entire accessible tree should be exposed with no caching.
      */
-    public static final Key<Boolean> FULL_ACCESSIBILITY_TREE =
+    private static final Key<Boolean> FULL_ACCESSIBILITY_TREE =
             new Key<Boolean>("fullAccessibilityTree", /* initOnly */ false, /* values */ null);
 
     private final GeckoSession mSession;
     private final GeckoBundle mBundle;
 
     public GeckoSessionSettings() {
         this(null, null);
     }
@@ -149,62 +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);
     }
 
-    public void setBoolean(final Key<Boolean> key, final boolean value) {
+
+    /**
+     * 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);
+    }
+
+    /**
+     * 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);
+    }
+
+
+    /**
+     * 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 getBoolean(final Key<Boolean> key) {
+    /**
+     * Whether tracking protection is enabled.
+     *
+     * @return true if tracking protection is enabled, false if not.
+     */
+    public boolean getUseTrackingProtection() {
+        return getBoolean(USE_TRACKING_PROTECTION);
+    }
+
+    /**
+     * Whether private mode is enabled.
+     *
+     * @return true if private mode is enabled, false if not.
+     */
+    public boolean getUsePrivateMode() {
+        return getBoolean(USE_PRIVATE_MODE);
+    }
+
+    /**
+     * Whether multiprocess is enabled.
+     *
+     * @return true if multiprocess is enabled, false if not.
+     */
+    public boolean getUseMultiprocess() {
+        return getBoolean(USE_MULTIPROCESS);
+    }
+
+    /**
+     * 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);
+    }
+
+    /**
+     * Whether javascript execution is allowed.
+     *
+     * @return true if javascript execution is allowed, false if not.
+     */
+    public boolean getAllowJavascript() {
+        return getBoolean(ALLOW_JAVASCRIPT);
+    }
+
+    /**
+     * 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 setInt(final Key<Integer> key, 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();
             }
         }
     }
 
-    public int getInt(final Key<Integer> key) {
+    /**
+     * 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 setString(final Key<String> key, 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();
             }
         }
     }
 
-    public String getString(final Key<String> key) {
+    /**
+     * 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().getBoolean(GeckoSessionSettings.FULL_ACCESSIBILITY_TREE) ?
+                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().getBoolean(GeckoSessionSettings.FULL_ACCESSIBILITY_TREE)) {
+        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);