Bug 1413698 - 2. Move GeckoView to GeckoSession; r=snorp
authorJim Chen <nchen@mozilla.com>
Mon, 06 Nov 2017 14:54:09 -0500
changeset 443657 2bb89a9a83080ef1bc41ebb18d28437b560cf1d8
parent 443656 1ed5527d3f111432704e1df9cb87d5c65c87ee19
child 443658 8ce593cc1b2d1bd0cfc6cc158f5211e35312e963
push id1618
push userCallek@gmail.com
push dateThu, 11 Jan 2018 17:45:48 +0000
treeherdermozilla-release@882ca853e05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs1413698
milestone58.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 1413698 - 2. Move GeckoView to GeckoSession; r=snorp Rename GeckoView to GeckoSession. Strip out parts of it that depended on being a subclass of View. Also strip out parts of it that dealt with switching EventDispatcher and NativeQueue, because now there's only one copy of each for each GeckoSession. MozReview-Commit-ID: J699twtpmTS
mobile/android/base/moz.build
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSession.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoViewSettings.java
widget/android/GeneratedJNINatives.h
widget/android/GeneratedJNIWrappers.cpp
widget/android/GeneratedJNIWrappers.h
widget/android/nsWindow.cpp
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -379,19 +379,19 @@ gvjar.sources += [geckoview_source_dir +
     'GeckoEditableClient.java',
     'GeckoEditableListener.java',
     'GeckoHalDefines.java',
     'GeckoInputConnection.java',
     'GeckoNetworkManager.java',
     'GeckoProfile.java',
     'GeckoProfileDirectories.java',
     'GeckoScreenOrientation.java',
+    'GeckoSession.java',
     'GeckoSharedPrefs.java',
     'GeckoThread.java',
-    'GeckoView.java',
     'GeckoViewHandler.java',
     'GeckoViewSettings.java',
     'gfx/BitmapUtils.java',
     'gfx/BufferedImage.java',
     'gfx/BufferedImageGLInfo.java',
     'gfx/DynamicToolbarAnimator.java',
     'gfx/FloatSize.java',
     'gfx/FullScreenState.java',
rename from mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
rename to mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSession.java
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSession.java
@@ -1,66 +1,48 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * vim: ts=4 sw=4 expandtab:
  * 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.gecko;
 
-import java.io.File;
 import java.net.URLConnection;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Set;
 
-import org.mozilla.gecko.annotation.ReflectionTarget;
 import org.mozilla.gecko.annotation.WrapForJNI;
-import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.mozglue.JNIObject;
-import org.mozilla.gecko.util.ActivityUtils;
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoBundle;
-import org.mozilla.gecko.util.ThreadUtils;
 
-import android.app.Activity;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.SharedPreferences;
 import android.content.res.Resources;
 import android.database.Cursor;
-import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
 import android.os.IBinder;
+import android.os.IInterface;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodManager;
 
-public class GeckoView extends LayerView {
-
-    private static final String DEFAULT_SHARED_PREFERENCES_FILE = "GeckoView";
-    private static final String LOGTAG = "GeckoView";
-
+public class GeckoSession implements Parcelable {
+    private static final String LOGTAG = "GeckoSession";
     private static final boolean DEBUG = false;
 
     /* package */ enum State implements NativeQueue.State {
-        @WrapForJNI INITIAL(0),
-        @WrapForJNI READY(1);
+        INITIAL(0),
+        READY(1);
 
         private final int mRank;
 
         private State(int rank) {
             mRank = rank;
         }
 
         @Override
@@ -70,17 +52,17 @@ public class GeckoView extends LayerView
 
         @Override
         public boolean isAtLeast(final NativeQueue.State other) {
             return (other instanceof State) &&
                    mRank >= ((State) other).mRank;
         }
     }
 
-    /* package */ final NativeQueue mNativeQueue =
+    private final NativeQueue mNativeQueue =
         new NativeQueue(State.INITIAL, State.READY);
 
     private final EventDispatcher mEventDispatcher =
         new EventDispatcher(mNativeQueue);
 
     private final GeckoViewHandler<ContentListener> mContentHandler =
         new GeckoViewHandler<ContentListener>(
             "GeckoViewContent", this,
@@ -93,28 +75,28 @@ public class GeckoView extends LayerView
         ) {
             @Override
             public void handleMessage(final ContentListener listener,
                                       final String event,
                                       final GeckoBundle message,
                                       final EventCallback callback) {
 
                 if ("GeckoView:ContextMenu".equals(event)) {
-                    listener.onContextMenu(GeckoView.this,
+                    listener.onContextMenu(GeckoSession.this,
                                            message.getInt("screenX"),
                                            message.getInt("screenY"),
                                            message.getString("uri"),
                                            message.getString("elementSrc"));
                 } else if ("GeckoView:DOMTitleChanged".equals(event)) {
-                    listener.onTitleChange(GeckoView.this,
+                    listener.onTitleChange(GeckoSession.this,
                                            message.getString("title"));
                 } else if ("GeckoView:FullScreenEnter".equals(event)) {
-                    listener.onFullScreen(GeckoView.this, true);
+                    listener.onFullScreen(GeckoSession.this, true);
                 } else if ("GeckoView:FullScreenExit".equals(event)) {
-                    listener.onFullScreen(GeckoView.this, false);
+                    listener.onFullScreen(GeckoSession.this, false);
                 }
             }
         };
 
     private final GeckoViewHandler<NavigationListener> mNavigationHandler =
         new GeckoViewHandler<NavigationListener>(
             "GeckoViewNavigation", this,
             new String[]{
@@ -123,29 +105,29 @@ public class GeckoView extends LayerView
             }
         ) {
             @Override
             public void handleMessage(final NavigationListener listener,
                                       final String event,
                                       final GeckoBundle message,
                                       final EventCallback callback) {
                 if ("GeckoView:LocationChange".equals(event)) {
-                    listener.onLocationChange(GeckoView.this,
+                    listener.onLocationChange(GeckoSession.this,
                                               message.getString("uri"));
-                    listener.onCanGoBack(GeckoView.this,
+                    listener.onCanGoBack(GeckoSession.this,
                                          message.getBoolean("canGoBack"));
-                    listener.onCanGoForward(GeckoView.this,
+                    listener.onCanGoForward(GeckoSession.this,
                                             message.getBoolean("canGoForward"));
                 } else if ("GeckoView:OnLoadUri".equals(event)) {
                     final String uri = message.getString("uri");
                     final NavigationListener.TargetWindow where =
                         NavigationListener.TargetWindow.forGeckoValue(
                             message.getInt("where"));
                     final boolean result =
-                        listener.onLoadUri(GeckoView.this, uri, where);
+                        listener.onLoadUri(GeckoSession.this, uri, where);
                     callback.sendSuccess(result);
                 }
             }
         };
 
     private final GeckoViewHandler<ProgressListener> mProgressHandler =
         new GeckoViewHandler<ProgressListener>(
             "GeckoViewProgress", this,
@@ -156,41 +138,41 @@ public class GeckoView extends LayerView
             }
         ) {
             @Override
             public void handleMessage(final ProgressListener listener,
                                       final String event,
                                       final GeckoBundle message,
                                       final EventCallback callback) {
                 if ("GeckoView:PageStart".equals(event)) {
-                    listener.onPageStart(GeckoView.this,
+                    listener.onPageStart(GeckoSession.this,
                                          message.getString("uri"));
                 } else if ("GeckoView:PageStop".equals(event)) {
-                    listener.onPageStop(GeckoView.this,
+                    listener.onPageStop(GeckoSession.this,
                                         message.getBoolean("success"));
                 } else if ("GeckoView:SecurityChanged".equals(event)) {
                     final GeckoBundle identity = message.getBundle("identity");
-                    listener.onSecurityChange(GeckoView.this, new ProgressListener.SecurityInformation(identity));
+                    listener.onSecurityChange(GeckoSession.this, new ProgressListener.SecurityInformation(identity));
                 }
             }
         };
 
     private final GeckoViewHandler<ScrollListener> mScrollHandler =
         new GeckoViewHandler<ScrollListener>(
             "GeckoViewScroll", this,
             new String[]{ "GeckoView:ScrollChanged" }
         ) {
             @Override
             public void handleMessage(final ScrollListener listener,
                                       final String event,
                                       final GeckoBundle message,
                                       final EventCallback callback) {
 
                 if ("GeckoView:ScrollChanged".equals(event)) {
-                    listener.onScrollChanged(GeckoView.this,
+                    listener.onScrollChanged(GeckoSession.this,
                                              message.getInt("scrollX"),
                                              message.getInt("scrollY"));
                 }
             }
         };
 
     private final GeckoViewHandler<PermissionDelegate> mPermissionHandler =
         new GeckoViewHandler<PermissionDelegate>(
@@ -208,27 +190,27 @@ public class GeckoView extends LayerView
                                       final EventCallback callback) {
 
                 if (listener == null) {
                     callback.sendSuccess(/* granted */ false);
                     return;
                 }
                 if ("GeckoView:AndroidPermission".equals(event)) {
                     listener.requestAndroidPermissions(
-                            GeckoView.this, message.getStringArray("perms"),
+                            GeckoSession.this, message.getStringArray("perms"),
                             new PermissionCallback("android", callback));
                 } else if ("GeckoView:ContentPermission".equals(event)) {
                     final String type = message.getString("perm");
                     listener.requestContentPermission(
-                            GeckoView.this, message.getString("uri"),
+                            GeckoSession.this, message.getString("uri"),
                             type, message.getString("access"),
                             new PermissionCallback(type, callback));
                 } else if ("GeckoView:MediaPermission".equals(event)) {
                     listener.requestMediaPermission(
-                            GeckoView.this, message.getString("uri"),
+                            GeckoSession.this, message.getString("uri"),
                             message.getBundleArray("video"), message.getBundleArray("audio"),
                             new PermissionCallback("media", callback));
                 }
             }
         };
 
     private static class PermissionCallback implements
         PermissionDelegate.Callback, PermissionDelegate.MediaCallback {
@@ -275,127 +257,73 @@ public class GeckoView extends LayerView
         @Override // PermissionDelegate.MediaCallback
         public void grant(final GeckoBundle video, final GeckoBundle audio) {
             grant(video != null ? video.getString("id") : null,
                   audio != null ? audio.getString("id") : null);
         }
     }
 
     /**
-     * Get the current prompt delegate for this GeckoView.
+     * Get the current prompt delegate for this GeckoSession.
      * @return PromptDelegate instance or null if using default delegate.
      */
     public PermissionDelegate getPermissionDelegate() {
         return mPermissionHandler.getListener();
     }
 
     /**
-     * Set the current permission delegate for this GeckoView.
+     * Set the current permission delegate for this GeckoSession.
      * @param delegate PermissionDelegate instance or null to use the default delegate.
      */
     public void setPermissionDelegate(final PermissionDelegate delegate) {
         mPermissionHandler.setListener(delegate, this);
     }
 
     private PromptDelegate mPromptDelegate;
-    private InputConnectionListener mInputConnectionListener;
-    private boolean mIsResettingFocus;
 
-    private GeckoViewSettings mSettings;
-
-    protected String mChromeUri;
-    protected int mScreenId = 0; // default to the primary screen
+    private final Listener mListener = new Listener();
 
-    @WrapForJNI(dispatchTo = "proxy")
-    protected static final class Window extends JNIObject {
-        @WrapForJNI(skip = true)
-        public final String chromeUri;
+    protected static final class Window extends JNIObject implements IInterface {
+        private final NativeQueue mNativeQueue;
+        private Binder mBinder;
 
-        @WrapForJNI(skip = true)
-        private NativeQueue mNativeQueue;
-
-        @WrapForJNI(skip = true)
-        /* package */ Window(final String chromeUri) {
-            this.chromeUri = chromeUri;
+        public Window(final NativeQueue nativeQueue) {
+            mNativeQueue = nativeQueue;
         }
 
+        @Override // IInterface
+        public IBinder asBinder() {
+            if (mBinder == null) {
+                mBinder = new Binder();
+                mBinder.attachInterface(this, Window.class.getName());
+            }
+            return mBinder;
+        }
+
+        @WrapForJNI(dispatchTo = "proxy")
         static native void open(Window instance, EventDispatcher dispatcher,
                                 GeckoBundle settings, String chromeUri,
                                 int screenId, boolean privateMode);
 
+        @WrapForJNI(dispatchTo = "proxy")
         @Override protected native void disposeNative();
 
+        @WrapForJNI(dispatchTo = "proxy")
         native void close();
 
-        native void attach(GeckoView view, Object compositor,
-                           EventDispatcher dispatcher);
+        @WrapForJNI(dispatchTo = "proxy")
+        native void attach(GeckoView view, Object compositor);
 
         @WrapForJNI(calledFrom = "gecko")
-        private synchronized void setState(final State newState) {
-            if (mNativeQueue.getState() != State.READY &&
-                newState == State.READY) {
+        private synchronized void onReady() {
+            if (mNativeQueue.checkAndSetState(State.INITIAL, State.READY)) {
                 Log.i(LOGTAG, "zerdatime " + SystemClock.elapsedRealtime() +
                       " - chrome startup finished");
             }
-            mNativeQueue.setState(newState);
         }
-
-        @WrapForJNI(calledFrom = "gecko")
-        private synchronized void onAttach(final GeckoView view) {
-            if (view.mNativeQueue == mNativeQueue) {
-                return;
-            } else if (mNativeQueue != null) {
-                view.mNativeQueue.setState(mNativeQueue.getState());
-            }
-            mNativeQueue = view.mNativeQueue;
-        }
-    }
-
-    // Object to hold onto our nsWindow connection when GeckoView gets destroyed.
-    private static class StateBinder extends Binder implements Parcelable {
-        public final Parcelable superState;
-        public final Window window;
-
-        public StateBinder(Parcelable superState, Window window) {
-            this.superState = superState;
-            this.window = window;
-        }
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel out, int flags) {
-            // Always write out the super-state, so that even if we lose this binder, we
-            // will still have something to pass into super.onRestoreInstanceState.
-            out.writeParcelable(superState, flags);
-            out.writeStrongBinder(this);
-        }
-
-        @ReflectionTarget
-        public static final Parcelable.Creator<StateBinder> CREATOR
-            = new Parcelable.Creator<StateBinder>() {
-                @Override
-                public StateBinder createFromParcel(Parcel in) {
-                    final Parcelable superState = in.readParcelable(null);
-                    final IBinder binder = in.readStrongBinder();
-                    if (binder instanceof StateBinder) {
-                        return (StateBinder) binder;
-                    }
-                    // Not the original object we saved; return null state.
-                    return new StateBinder(superState, null);
-                }
-
-                @Override
-                public StateBinder[] newArray(int size) {
-                    return new StateBinder[size];
-                }
-            };
     }
 
     private class Listener implements BundleEventListener {
         /* package */ void registerListeners() {
             getEventDispatcher().registerUiThreadListener(this,
                 "GeckoView:Prompt",
                 null);
         }
@@ -403,149 +331,188 @@ public class GeckoView extends LayerView
         @Override
         public void handleMessage(final String event, final GeckoBundle message,
                                   final EventCallback callback) {
             if (DEBUG) {
                 Log.d(LOGTAG, "handleMessage: event = " + event);
             }
 
             if ("GeckoView:Prompt".equals(event)) {
-                handlePromptEvent(GeckoView.this, message, callback);
+                handlePromptEvent(GeckoSession.this, message, callback);
             }
         }
     }
 
     protected Window mWindow;
-    private boolean mStateSaved;
-    private final Listener mListener = new Listener();
+    private GeckoViewSettings mSettings;
+    private String mChromeUri;
+    private int mScreenId = 0; // default to the primary screen
 
-    public GeckoView(Context context) {
-        super(context);
-
-        init(context, null);
+    public GeckoSession() {
+        this(/* settings */ null);
     }
 
-    public GeckoView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        // TODO: Convert custom attributes to GeckoViewSettings
-        init(context, null);
+    public GeckoSession(final GeckoViewSettings settings) {
+        if (settings == null) {
+            mSettings = new GeckoViewSettings(mEventDispatcher);
+        } else {
+            mSettings = new GeckoViewSettings(settings, mEventDispatcher);
+        }
+
+        mListener.registerListeners();
+    }
+
+    /* package */ void transferFrom(final GeckoSession session) {
+        if (isOpen()) {
+            throw new IllegalStateException("Session is open");
+        }
+        mWindow = session.mWindow;
+        session.mWindow = null;
+
+        mSettings = new GeckoViewSettings(session.mSettings, getEventDispatcher());
+        mChromeUri = session.mChromeUri;
+        mScreenId = session.mScreenId;
+    }
+
+    @Override // Parcelable
+    public int describeContents() {
+        return 0;
     }
 
-    public GeckoView(Context context, final GeckoViewSettings settings) {
-        super(context);
-
-        final GeckoViewSettings newSettings = new GeckoViewSettings(settings, getEventDispatcher());
-        init(context, settings);
+    @Override // Parcelable
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeStrongInterface(mWindow);
+        out.writeParcelable(mSettings, flags);
+        out.writeString(mChromeUri);
+        out.writeInt(mScreenId);
     }
 
-    public GeckoView(Context context, AttributeSet attrs, final GeckoViewSettings settings) {
-        super(context, attrs);
+    // AIDL code may call readFromParcel even though it's not part of Parcelable.
+    public void readFromParcel(final Parcel source) {
+        if (isOpen()) {
+            throw new IllegalStateException("Session is open");
+        }
 
-        final GeckoViewSettings newSettings = new GeckoViewSettings(settings, getEventDispatcher());
-        init(context, newSettings);
+        final IBinder binder = source.readStrongBinder();
+        final IInterface window = (binder != null) ?
+                binder.queryLocalInterface(Window.class.getName()) : null;
+        if (window instanceof Window) {
+            mWindow = (Window) window;
+        } else {
+            mWindow = null;
+        }
+
+        final GeckoViewSettings settings =
+                source.readParcelable(getClass().getClassLoader());
+        mSettings = new GeckoViewSettings(settings, getEventDispatcher());
+
+        mChromeUri = source.readString();
+        mScreenId = source.readInt();
     }
 
+    public static final Creator<GeckoSession> CREATOR = new Creator<GeckoSession>() {
+        @Override
+        public GeckoSession createFromParcel(final Parcel in) {
+            final GeckoSession session = new GeckoSession();
+            session.readFromParcel(in);
+            return session;
+        }
+
+        @Override
+        public GeckoSession[] newArray(final int size) {
+            return new GeckoSession[size];
+        }
+    };
+
     /**
-     * Preload GeckoView by starting Gecko in the background, if Gecko is not already running.
+     * Preload GeckoSession by starting Gecko in the background, if Gecko is not already running.
      *
-     * @param context Activity or Application Context for starting GeckoView.
+     * @param context Activity or Application Context for starting GeckoSession.
      */
     public static void preload(final Context context) {
         preload(context, /* geckoArgs */ null, /* multiprocess */ false);
     }
 
     /**
-     * Preload GeckoView by starting Gecko with the specified arguments in the background,
+     * Preload GeckoSession by starting Gecko with the specified arguments in the background,
      * if Gecko is not already running.
      *
-     * @param context Activity or Application Context for starting GeckoView.
+     * @param context Activity or Application Context for starting GeckoSession.
      * @param geckoArgs Arguments to be passed to Gecko, if Gecko is not already running.
      * @param multiprocess True if child process in multiprocess mode should be preloaded.
      */
     public static void preload(final Context context, final String geckoArgs,
                                final boolean multiprocess) {
         final Context appContext = context.getApplicationContext();
         if (GeckoAppShell.getApplicationContext() == null) {
             GeckoAppShell.setApplicationContext(appContext);
         }
 
         final int flags = multiprocess ? GeckoThread.FLAG_PRELOAD_CHILD : 0;
         if (GeckoThread.initMainProcess(/* profile */ null, geckoArgs, flags)) {
             GeckoThread.launch();
         }
     }
 
-    private void init(final Context context, final GeckoViewSettings settings) {
-        final boolean multiprocess = settings != null &&
-                                     settings.getBoolean(GeckoViewSettings.USE_MULTIPROCESS);
-        preload(context, /* geckoArgs */ null, multiprocess);
-
-        initializeView();
-        mListener.registerListeners();
-
-        if (settings == null) {
-            mSettings = new GeckoViewSettings(getEventDispatcher());
-        } else {
-            mSettings = settings;
-        }
-        mSettings.setString(GeckoViewSettings.DEBUGGER_SOCKET_DIR,
-                            context.getApplicationInfo().dataDir);
-
-    }
-
-    @Override
-    protected Parcelable onSaveInstanceState() {
-        final Parcelable superState = super.onSaveInstanceState();
-        mStateSaved = true;
-        return new StateBinder(superState, mWindow);
-    }
-
-    @Override
-    protected void onRestoreInstanceState(final Parcelable state) {
-        final StateBinder stateBinder = (StateBinder) state;
-
-        if (stateBinder.window != null) {
-            mWindow = stateBinder.window;
-        }
-        mStateSaved = false;
-
-        // We have to always call super.onRestoreInstanceState because View keeps
-        // track of these calls and throws an exception when we don't call it.
-        super.onRestoreInstanceState(stateBinder.superState);
-    }
-
     /**
      * Return the URI of the underlying chrome window opened or to be opened, or null if
-     * using the default GeckoView URI.
+     * using the default GeckoSession URI.
      *
      * @return Current chrome URI or null.
      */
     public String getChromeUri() {
-        if (mWindow != null) {
-            return mWindow.chromeUri;
-        }
         return mChromeUri;
     }
 
     /**
      * Set the URI of the underlying chrome window to be opened, or null to use the
-     * default GeckoView URI. Can only be called before the chrome window is opened during
+     * default GeckoSession URI. Can only be called before the chrome window is opened during
      * {@link #onAttachedToWindow}.
      *
      * @param uri New chrome URI or null.
      */
     public void setChromeUri(final String uri) {
-        if (mWindow != null) {
-            throw new IllegalStateException("Already opened chrome window");
+        if (isOpen()) {
+            throw new IllegalStateException("Session is open");
         }
         mChromeUri = uri;
     }
 
-    protected void openWindow() {
-        mWindow = new Window(mChromeUri);
+    public int getScreenId() {
+        return mScreenId;
+    }
+
+    public void setScreenId(final int id) {
+        if (isOpen()) {
+            throw new IllegalStateException("Session is open");
+        }
+        mScreenId = id;
+    }
+
+    public boolean isOpen() {
+        return mWindow != null;
+    }
+
+    public void openWindow(final Context appContext) {
+        if (isOpen()) {
+            throw new IllegalStateException("Session is open");
+        }
+
+        if (!GeckoThread.isLaunched()) {
+            final boolean multiprocess =
+                    mSettings.getBoolean(GeckoViewSettings.USE_MULTIPROCESS);
+            preload(appContext, /* geckoArgs */ null, multiprocess);
+        }
+
+        if (mSettings.getString(GeckoViewSettings.DEBUGGER_SOCKET_DIR) == null) {
+            mSettings.setString(GeckoViewSettings.DEBUGGER_SOCKET_DIR,
+                                appContext.getApplicationInfo().dataDir);
+        }
+
+        mWindow = new Window(mNativeQueue);
 
         if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
             Window.open(mWindow, mEventDispatcher, mSettings.asBundle(),
                         mChromeUri, mScreenId,
                         mSettings.getBoolean(GeckoViewSettings.USE_PRIVATE_MODE));
         } else {
             GeckoThread.queueNativeCallUntil(
                 GeckoThread.State.PROFILE_READY,
@@ -553,59 +520,43 @@ public class GeckoView extends LayerView
                 Window.class, mWindow,
                 EventDispatcher.class, mEventDispatcher,
                 GeckoBundle.class, mSettings.asBundle(),
                 String.class, mChromeUri,
                 mScreenId, mSettings.getBoolean(GeckoViewSettings.USE_PRIVATE_MODE));
         }
     }
 
-    protected void attachView() {
+    public void attachView(final GeckoView view) {
+        if (view == null) {
+            return;
+        }
+
         if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
-            mWindow.attach(this, getCompositor(), mEventDispatcher);
+            mWindow.attach(view, view.getCompositor());
         } else {
             GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
-                    mWindow, "attach", GeckoView.class, this,
-                    Object.class, getCompositor(), EventDispatcher.class, mEventDispatcher);
+                    mWindow, "attach",
+                    GeckoView.class, view,
+                    Object.class, view.getCompositor());
         }
     }
 
-    @Override
-    public void onAttachedToWindow() {
-        final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
-
-        if (mWindow == null) {
-            // Open a new nsWindow if we didn't have one from before.
-            openWindow();
-        }
-
-        attachView();
-
-        super.onAttachedToWindow();
-    }
-
-    @Override
-    public void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        super.destroy();
-
-        if (mStateSaved) {
-            // If we saved state earlier, we don't want to close the nsWindow.
-            return;
-        }
-
+    public void closeWindow() {
         if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
             mWindow.close();
             mWindow.disposeNative();
         } else {
             GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
                     mWindow, "close");
             GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
                     mWindow, "disposeNative");
         }
+
+        mWindow = null;
     }
 
     /**
     * Load the given URI.
     * @param uri The URI of the resource to load.
     */
     public void loadUri(String uri) {
         final GeckoBundle msg = new GeckoBundle();
@@ -630,156 +581,46 @@ public class GeckoView extends LayerView
 
     /**
     * Stop loading.
     */
     public void stop() {
         mEventDispatcher.dispatch("GeckoView:Stop", null);
     }
 
-    /* package */ void setInputConnectionListener(final InputConnectionListener icl) {
-        mInputConnectionListener = icl;
-    }
-
     /**
     * Go back in history.
     */
     public void goBack() {
         mEventDispatcher.dispatch("GeckoView:GoBack", null);
     }
 
     /**
     * Go forward in history.
     */
     public void goForward() {
         mEventDispatcher.dispatch("GeckoView:GoForward", null);
     }
 
     /**
-    * Set this GeckoView as active or inactive. Setting a GeckoView to inactive will
+    * Set this GeckoSession as active or inactive. Setting a GeckoSession to inactive will
     * significantly reduce its memory footprint, but should only be done if the
-    * GeckoView is not currently visible.
-    * @param active A boolean determining whether the GeckoView is active
+    * GeckoSession is not currently visible.
+    * @param active A boolean determining whether the GeckoSession is active
     */
     public void setActive(boolean active) {
         final GeckoBundle msg = new GeckoBundle();
         msg.putBoolean("active", active);
         mEventDispatcher.dispatch("GeckoView:SetActive", msg);
     }
 
     public GeckoViewSettings getSettings() {
         return mSettings;
     }
 
-    @Override
-    public void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
-        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
-
-        if (gainFocus && !mIsResettingFocus) {
-            ThreadUtils.postToUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    if (!isFocused()) {
-                        return;
-                    }
-
-                    final InputMethodManager imm = InputMethods.getInputMethodManager(getContext());
-                    // Bug 1404111:
-                    // Through View#onFocusChanged, the InputMethodManager queues up a checkFocus
-                    // call for the next spin of the message loop, so by posting this Runnable after
-                    // super#onFocusChanged, the IMM should have completed its focus change handling
-                    // at this point and we should be the active view for input handling.
-
-                    // If however onViewDetachedFromWindow for the previously active view gets
-                    // called *after* onFocusChanged, but *before* the focus change has been fully
-                    // processed by the IMM with the help of checkFocus, the IMM will lose track of
-                    // the currently active view, which means that we can't interact with the IME.
-                    if (!imm.isActive(GeckoView.this)) {
-                        // If that happens, we bring the IMM's internal state back into sync by
-                        // clearing and resetting our focus.
-                        mIsResettingFocus = true;
-                        clearFocus();
-                        // After calling clearFocus we might regain focus automatically, but we
-                        // explicitly request it again in case this doesn't happen.
-                        // If we've already got the focus back, this will then be a no-op anyway.
-                        requestFocus();
-                        mIsResettingFocus = false;
-                    }
-                }
-            });
-        }
-    }
-
-    @Override
-    public Handler getHandler() {
-        if (mInputConnectionListener != null) {
-            return mInputConnectionListener.getHandler(super.getHandler());
-        }
-        return super.getHandler();
-    }
-
-    @Override
-    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
-        if (mInputConnectionListener != null) {
-            return mInputConnectionListener.onCreateInputConnection(outAttrs);
-        }
-        return null;
-    }
-
-    @Override
-    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
-        if (super.onKeyPreIme(keyCode, event)) {
-            return true;
-        }
-        return mInputConnectionListener != null &&
-                mInputConnectionListener.onKeyPreIme(keyCode, event);
-    }
-
-    @Override
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
-        if (super.onKeyUp(keyCode, event)) {
-            return true;
-        }
-        return mInputConnectionListener != null &&
-                mInputConnectionListener.onKeyUp(keyCode, event);
-    }
-
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        if (super.onKeyDown(keyCode, event)) {
-            return true;
-        }
-        return mInputConnectionListener != null &&
-                mInputConnectionListener.onKeyDown(keyCode, event);
-    }
-
-    @Override
-    public boolean onKeyLongPress(int keyCode, KeyEvent event) {
-        if (super.onKeyLongPress(keyCode, event)) {
-            return true;
-        }
-        return mInputConnectionListener != null &&
-                mInputConnectionListener.onKeyLongPress(keyCode, event);
-    }
-
-    @Override
-    public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
-        if (super.onKeyMultiple(keyCode, repeatCount, event)) {
-            return true;
-        }
-        return mInputConnectionListener != null &&
-                mInputConnectionListener.onKeyMultiple(keyCode, repeatCount, event);
-    }
-
-    @Override
-    public boolean isIMEEnabled() {
-        return mInputConnectionListener != null &&
-                mInputConnectionListener.isIMEEnabled();
-    }
-
     public void importScript(final String url) {
         if (url.startsWith("resource://android/assets/")) {
             final GeckoBundle data = new GeckoBundle(1);
             data.putString("scriptURL", url);
             getEventDispatcher().dispatch("GeckoView:ImportScript", data);
             return;
         }
 
@@ -849,25 +690,25 @@ public class GeckoView extends LayerView
     * This will replace the current handler.
     * @param listener An implementation of ScrollListener.
     */
     public void setScrollListener(ScrollListener listener) {
         mScrollHandler.setListener(listener, this);
     }
 
     /**
-     * Set the current prompt delegate for this GeckoView.
+     * Set the current prompt delegate for this GeckoSession.
      * @param delegate PromptDelegate instance or null to use the built-in delegate.
      */
     public void setPromptDelegate(PromptDelegate delegate) {
         mPromptDelegate = delegate;
     }
 
     /**
-     * Get the current prompt delegate for this GeckoView.
+     * Get the current prompt delegate for this GeckoSession.
      * @return PromptDelegate instance or null if using built-in delegate.
      */
     public PromptDelegate getPromptDelegate() {
         return mPromptDelegate;
     }
 
     private static class PromptCallback implements
         PromptDelegate.AlertCallback, PromptDelegate.ButtonCallback,
@@ -1085,34 +926,34 @@ public class GeckoView extends LayerView
                 ensureResult().putStringArray("files", paths);
             } else {
                 throw new UnsupportedOperationException();
             }
             submit();
         }
     }
 
-    /* package */ static void handlePromptEvent(final GeckoView view,
+    /* package */ static void handlePromptEvent(final GeckoSession session,
                                                 final GeckoBundle message,
                                                 final EventCallback callback) {
-        final PromptDelegate delegate = view.getPromptDelegate();
+        final PromptDelegate delegate = session.getPromptDelegate();
         if (delegate == null) {
             // Default behavior is same as calling dismiss() on callback.
             callback.sendSuccess(null);
             return;
         }
 
         final String type = message.getString("type");
         final String mode = message.getString("mode");
         final PromptCallback cb = new PromptCallback(type, mode, message, callback);
         final String title = message.getString("title");
         final String msg = message.getString("msg");
         switch (type) {
             case "alert": {
-                delegate.alert(view, title, msg, cb);
+                delegate.alert(session, title, msg, cb);
                 break;
             }
             case "button": {
                 final String[] btnTitle = message.getStringArray("btnTitle");
                 final String[] btnCustomTitle = message.getStringArray("btnCustomTitle");
                 for (int i = 0; i < btnCustomTitle.length; i++) {
                     final int resId;
                     if ("ok".equals(btnTitle[i])) {
@@ -1123,45 +964,45 @@ public class GeckoView extends LayerView
                         resId = android.R.string.yes;
                     } else if ("no".equals(btnTitle[i])) {
                         resId = android.R.string.no;
                     } else {
                         continue;
                     }
                     btnCustomTitle[i] = Resources.getSystem().getString(resId);
                 }
-                delegate.promptForButton(view, title, msg, btnCustomTitle, cb);
+                delegate.promptForButton(session, title, msg, btnCustomTitle, cb);
                 break;
             }
             case "text": {
-                delegate.promptForText(view, title, msg, message.getString("value"), cb);
+                delegate.promptForText(session, title, msg, message.getString("value"), cb);
                 break;
             }
             case "auth": {
-                delegate.promptForAuth(view, title, msg, message.getBundle("options"), cb);
+                delegate.promptForAuth(session, title, msg, message.getBundle("options"), cb);
                 break;
             }
             case "choice": {
                 final int intMode;
                 if ("menu".equals(mode)) {
                     intMode = PromptDelegate.CHOICE_TYPE_MENU;
                 } else if ("single".equals(mode)) {
                     intMode = PromptDelegate.CHOICE_TYPE_SINGLE;
                 } else if ("multiple".equals(mode)) {
                     intMode = PromptDelegate.CHOICE_TYPE_MULTIPLE;
                 } else {
                     callback.sendError("Invalid mode");
                     return;
                 }
-                delegate.promptForChoice(view, title, msg, intMode,
+                delegate.promptForChoice(session, title, msg, intMode,
                                          message.getBundleArray("choices"), cb);
                 break;
             }
             case "color": {
-                delegate.promptForColor(view, title, message.getString("value"), cb);
+                delegate.promptForColor(session, title, message.getString("value"), cb);
                 break;
             }
             case "datetime": {
                 final int intMode;
                 if ("date".equals(mode)) {
                     intMode = PromptDelegate.DATETIME_TYPE_DATE;
                 } else if ("month".equals(mode)) {
                     intMode = PromptDelegate.DATETIME_TYPE_MONTH;
@@ -1170,17 +1011,17 @@ public class GeckoView extends LayerView
                 } else if ("time".equals(mode)) {
                     intMode = PromptDelegate.DATETIME_TYPE_TIME;
                 } else if ("datetime-local".equals(mode)) {
                     intMode = PromptDelegate.DATETIME_TYPE_DATETIME_LOCAL;
                 } else {
                     callback.sendError("Invalid mode");
                     return;
                 }
-                delegate.promptForDateTime(view, title, intMode,
+                delegate.promptForDateTime(session, title, intMode,
                                            message.getString("value"),
                                            message.getString("min"),
                                            message.getString("max"), cb);
                 break;
             }
             case "file": {
                 final int intMode;
                 if ("single".equals(mode)) {
@@ -1201,17 +1042,17 @@ public class GeckoView extends LayerView
                         final String mimeType =
                                 URLConnection.guessContentTypeFromName(extension);
                         if (mimeType != null) {
                             combined.add(mimeType);
                         }
                     }
                     mimeTypes = combined.toArray(new String[combined.size()]);
                 }
-                delegate.promptForFile(view, title, intMode, mimeTypes, cb);
+                delegate.promptForFile(session, title, intMode, mimeTypes, cb);
                 break;
             }
             default: {
                 callback.sendError("Invalid type");
                 break;
             }
         }
     }
@@ -1303,94 +1144,94 @@ public class GeckoView extends LayerView
                 subjectName = identityData.getString("subjectName");
                 issuerCommonName = identityData.getString("issuerCommonName");
                 issuerOrganization = identityData.getString("issuerOrganization");
             }
         }
 
         /**
         * A View has started loading content from the network.
-        * @param view The GeckoView that initiated the callback.
+        * @param view The GeckoSession that initiated the callback.
         * @param url The resource being loaded.
         */
-        void onPageStart(GeckoView view, String url);
+        void onPageStart(GeckoSession session, String url);
 
         /**
         * A View has finished loading content from the network.
-        * @param view The GeckoView that initiated the callback.
+        * @param view The GeckoSession that initiated the callback.
         * @param success Whether the page loaded successfully or an error occurred.
         */
-        void onPageStop(GeckoView view, boolean success);
+        void onPageStop(GeckoSession session, boolean success);
 
         /**
         * The security status has been updated.
-        * @param view The GeckoView that initiated the callback.
+        * @param view The GeckoSession that initiated the callback.
         * @param securityInfo The new security information.
         */
-        void onSecurityChange(GeckoView view, SecurityInformation securityInfo);
+        void onSecurityChange(GeckoSession session, SecurityInformation securityInfo);
     }
 
     public interface ContentListener {
         /**
         * A page title was discovered in the content or updated after the content
         * loaded.
-        * @param view The GeckoView that initiated the callback.
+        * @param view The GeckoSession that initiated the callback.
         * @param title The title sent from the content.
         */
-        void onTitleChange(GeckoView view, String title);
+        void onTitleChange(GeckoSession session, String title);
 
         /**
          * A page has entered or exited full screen mode. Typically, the implementation
-         * would set the Activity containing the GeckoView to full screen when the page is
+         * would set the Activity containing the GeckoSession to full screen when the page is
          * in full screen mode.
          *
-         * @param view The GeckoView that initiated the callback.
+         * @param view The GeckoSession that initiated the callback.
          * @param fullScreen True if the page is in full screen mode.
          */
-        void onFullScreen(GeckoView view, boolean fullScreen);
+        void onFullScreen(GeckoSession session, boolean fullScreen);
 
 
         /**
          * A user has initiated the context menu via long-press.
          * This event is fired on links, (nested) images and (nested) media
          * elements.
          *
-         * @param view The GeckoView that initiated the callback.
+         * @param view The GeckoSession that initiated the callback.
          * @param screenX The screen coordinates of the press.
          * @param screenY The screen coordinates of the press.
          * @param uri The URI of the pressed link, set for links and
          *            image-links.
          * @param elementSrc The source URI of the pressed element, set for
          *                   (nested) images and media elements.
          */
-        void onContextMenu(GeckoView view, int screenX, int screenY,
+        void onContextMenu(GeckoSession session, int screenX, int screenY,
                            String uri, String elementSrc);
     }
 
     public interface NavigationListener {
         /**
         * A view has started loading content from the network.
-        * @param view The GeckoView that initiated the callback.
+        * @param view The GeckoSession that initiated the callback.
         * @param url The resource being loaded.
         */
-        void onLocationChange(GeckoView view, String url);
+        void onLocationChange(GeckoSession session, String url);
 
         /**
         * The view's ability to go back has changed.
-        * @param view The GeckoView that initiated the callback.
+        * @param view The GeckoSession that initiated the callback.
         * @param canGoBack The new value for the ability.
         */
-        void onCanGoBack(GeckoView view, boolean canGoBack);
+        void onCanGoBack(GeckoSession session, boolean canGoBack);
 
         /**
         * The view's ability to go forward has changed.
-        * @param view The GeckoView that initiated the callback.
+        * @param view The GeckoSession that initiated the callback.
         * @param canGoForward The new value for the ability.
         */
-        void onCanGoForward(GeckoView view, boolean canGoForward);
+        void onCanGoForward(GeckoSession session, boolean canGoForward);
 
         enum TargetWindow {
             DEFAULT(0),
             CURRENT(1),
             NEW(2);
 
             private static final TargetWindow[] sValues = TargetWindow.values();
             private int mValue;
@@ -1428,29 +1269,29 @@ public class GeckoView extends LayerView
 
             private LoadUriResult(int value) {
                 mValue = value;
             }
         }
 
         /**
         * A request to open an URI.
-        * @param view The GeckoView that initiated the callback.
+        * @param view The GeckoSession that initiated the callback.
         * @param uri The URI to be loaded.
         * @param where The target window.
         *
         * @return True if the URI loading has been handled, false if Gecko
         *         should handle the loading.
         */
-        boolean onLoadUri(GeckoView view, String uri, TargetWindow where);
+        boolean onLoadUri(GeckoSession session, String uri, TargetWindow where);
     }
 
     /**
-     * GeckoView applications implement this interface to handle prompts triggered by
-     * content in the GeckoView, such as alerts, authentication dialogs, and select list
+     * GeckoSession applications implement this interface to handle prompts triggered by
+     * content in the GeckoSession, such as alerts, authentication dialogs, and select list
      * pickers.
      **/
     public interface PromptDelegate {
         /**
          * Callback interface for notifying the result of a prompt, and for accessing the
          * optional features for prompts (e.g. optional checkbox).
          */
         interface AlertCallback {
@@ -1484,23 +1325,23 @@ public class GeckoView extends LayerView
              * Set the current value for the optional checkbox.
              */
             void setCheckboxValue(boolean value);
         }
 
         /**
          * Display a simple message prompt.
          *
-         * @param view The GeckoView that triggered the prompt
+         * @param view The GeckoSession that triggered the prompt
          *             or null if the prompt is a global prompt.
          * @param title Title for the prompt dialog.
          * @param msg Message for the prompt dialog.
          * @param callback Callback interface.
          */
-        void alert(GeckoView view, String title, String msg, AlertCallback callback);
+        void alert(GeckoSession session, String title, String msg, AlertCallback callback);
 
         /**
          * Callback interface for notifying the result of a button prompt.
          */
         interface ButtonCallback extends AlertCallback {
             /**
              * Called by the prompt implementation when the button prompt is dismissed by
              * the user pressing one of the buttons.
@@ -1510,28 +1351,28 @@ public class GeckoView extends LayerView
 
         static final int BUTTON_TYPE_POSITIVE = 0;
         static final int BUTTON_TYPE_NEUTRAL = 1;
         static final int BUTTON_TYPE_NEGATIVE = 2;
 
         /**
          * Display a prompt with up to three buttons.
          *
-         * @param view The GeckoView that triggered the prompt
+         * @param view The GeckoSession that triggered the prompt
          *             or null if the prompt is a global prompt.
          * @param title Title for the prompt dialog.
          * @param msg Message for the prompt dialog.
          * @param btnMsg Array of 3 elements indicating labels for the individual buttons.
          *               btnMsg[BUTTON_TYPE_POSITIVE] is the label for the "positive" button.
          *               btnMsg[BUTTON_TYPE_NEUTRAL] is the label for the "neutral" button.
          *               btnMsg[BUTTON_TYPE_NEGATIVE] is the label for the "negative" button.
          *               The button is hidden if the corresponding label is null.
          * @param callback Callback interface.
          */
-        void promptForButton(GeckoView view, String title, String msg,
+        void promptForButton(GeckoSession session, String title, String msg,
                              String[] btnMsg, ButtonCallback callback);
 
         /**
          * Callback interface for notifying the result of prompts that have text results,
          * including color and date/time pickers.
          */
         interface TextCallback extends AlertCallback {
             /**
@@ -1539,24 +1380,24 @@ public class GeckoView extends LayerView
              * the user, for example by pressing the "OK" button.
              */
             void confirm(String text);
         }
 
         /**
          * Display a prompt for inputting text.
          *
-         * @param view The GeckoView that triggered the prompt
+         * @param view The GeckoSession that triggered the prompt
          *             or null if the prompt is a global prompt.
          * @param title Title for the prompt dialog.
          * @param msg Message for the prompt dialog.
          * @param value Default input text for the prompt.
          * @param callback Callback interface.
          */
-        void promptForText(GeckoView view, String title, String msg,
+        void promptForText(GeckoSession session, String title, String msg,
                            String value, TextCallback callback);
 
         /**
          * Callback interface for notifying the result of authentication prompts.
          */
         interface AuthCallback extends AlertCallback {
             /**
              * Called by the prompt implementation when a password-only prompt is
@@ -1603,29 +1444,29 @@ public class GeckoView extends LayerView
         /**
          * The auth request encrypts both password and data.
          */
         static final int AUTH_LEVEL_SECURE = 2;
 
         /**
          * Display a prompt for authentication credentials.
          *
-         * @param view The GeckoView that triggered the prompt
+         * @param view The GeckoSession that triggered the prompt
          *             or null if the prompt is a global prompt.
          * @param title Title for the prompt dialog.
          * @param msg Message for the prompt dialog.
          * @param options Bundle containing options for the prompt with keys,
          *                "flags": int, bit field of AUTH_FLAG_* flags;
          *                "uri": String, URI for the auth request or null if unknown;
          *                "level": int, one of AUTH_LEVEL_* indicating level of encryption;
          *                "username": String, initial username or null if password-only;
          *                "password": String, intiial password;
          * @param callback Callback interface.
          */
-        void promptForAuth(GeckoView view, String title, String msg,
+        void promptForAuth(GeckoSession session, String title, String msg,
                            GeckoBundle options, AuthCallback callback);
 
         /**
          * Callback interface for notifying the result of menu or list choice.
          */
         interface ChoiceCallback extends AlertCallback {
             /**
              * Called by the prompt implementation when the menu or single-choice list is
@@ -1675,17 +1516,17 @@ public class GeckoView extends LayerView
         /**
          * Display choices in a list that allows multiple selections.
          */
         static final int CHOICE_TYPE_MULTIPLE = 3;
 
         /**
          * Display a menu prompt or list prompt.
          *
-         * @param view The GeckoView that triggered the prompt
+         * @param view The GeckoSession that triggered the prompt
          *             or null if the prompt is a global prompt.
          * @param title Title for the prompt dialog, or null for no title.
          * @param msg Message for the prompt dialog, or null for no message.
          * @param type One of CHOICE_TYPE_* indicating the type of prompt.
          * @param choices Array of bundles each representing an item or group, with keys,
          *                "disabled": boolean, true if the item should not be selectable;
          *                "icon": String, URI of the item icon or null if none
          *                        (only valid for menus);
@@ -1694,30 +1535,30 @@ public class GeckoView extends LayerView
          *                         if not a group.
          *                "label": String, label for displaying the item or group;
          *                "selected": boolean, true if the item should be pre-selected
          *                            (pre-checked for menu items);
          *                "separator": boolean, true if the item should be a menu separator
          *                             (only valid for menus);
          * @param callback Callback interface.
          */
-        void promptForChoice(GeckoView view, String title, String msg, int type,
+        void promptForChoice(GeckoSession session, String title, String msg, int type,
                              GeckoBundle[] choices, ChoiceCallback callback);
 
         /**
          * Display a color prompt.
          *
-         * @param view The GeckoView that triggered the prompt
+         * @param view The GeckoSession that triggered the prompt
          *             or null if the prompt is a global prompt.
          * @param title Title for the prompt dialog.
          * @param value Initial color value in HTML color format.
          * @param callback Callback interface; the result passed to confirm() must be in
          *                 HTML color format.
          */
-        void promptForColor(GeckoView view, String title, String value,
+        void promptForColor(GeckoSession session, String title, String value,
                             TextCallback callback);
 
         /**
          * Prompt for year, month, and day.
          */
         static final int DATETIME_TYPE_DATE = 1;
 
         /**
@@ -1738,27 +1579,27 @@ public class GeckoView extends LayerView
         /**
          * Prompt for year, month, day, hour, and minute, without timezone.
          */
         static final int DATETIME_TYPE_DATETIME_LOCAL = 5;
 
         /**
          * Display a date/time prompt.
          *
-         * @param view The GeckoView that triggered the prompt
+         * @param view The GeckoSession that triggered the prompt
          *             or null if the prompt is a global prompt.
          * @param title Title for the prompt dialog; currently always null.
          * @param type One of DATETIME_TYPE_* indicating the type of prompt.
          * @param value Initial date/time value in HTML date/time format.
          * @param min Minimum date/time value in HTML date/time format.
          * @param max Maximum date/time value in HTML date/time format.
          * @param callback Callback interface; the result passed to confirm() must be in
          *                 HTML date/time format.
          */
-        void promptForDateTime(GeckoView view, String title, int type,
+        void promptForDateTime(GeckoSession session, String title, int type,
                                String value, String min, String max, TextCallback callback);
 
         /**
          * Callback interface for notifying the result of file prompts.
          */
         interface FileCallback extends AlertCallback {
             /**
              * Called by the prompt implementation when the user makes a file selection in
@@ -1780,46 +1621,46 @@ public class GeckoView extends LayerView
         }
 
         static final int FILE_TYPE_SINGLE = 1;
         static final int FILE_TYPE_MULTIPLE = 2;
 
         /**
          * Display a file prompt.
          *
-         * @param view The GeckoView that triggered the prompt
+         * @param view The GeckoSession that triggered the prompt
          *             or null if the prompt is a global prompt.
          * @param title Title for the prompt dialog.
          * @param type One of FILE_TYPE_* indicating the prompt type.
          * @param mimeTypes Array of permissible MIME types for the selected files, in
          *                  the form "type/subtype", where "type" and/or "subtype" can be
          *                  "*" to indicate any value.
          * @param callback Callback interface.
          */
-        void promptForFile(GeckoView view, String title, int type,
+        void promptForFile(GeckoSession session, String title, int type,
                            String[] mimeTypes, FileCallback callback);
     }
 
     /**
-     * GeckoView applications implement this interface to handle content scroll
+     * GeckoSession applications implement this interface to handle content scroll
      * events.
      **/
     public interface ScrollListener {
         /**
          * The scroll position of the content has changed.
          *
-        * @param view The GeckoView that initiated the callback.
+        * @param view The GeckoSession that initiated the callback.
         * @param scrollX The new horizontal scroll position in pixels.
         * @param scrollY The new vertical scroll position in pixels.
         */
-        public void onScrollChanged(GeckoView view, int scrollX, int scrollY);
+        public void onScrollChanged(GeckoSession session, int scrollX, int scrollY);
     }
 
     /**
-     * GeckoView applications implement this interface to handle requests for permissions
+     * GeckoSession applications implement this interface to handle requests for permissions
      * from content, such as geolocation and notifications. For each permission, usually
      * two requests are generated: one request for the Android app permission through
      * requestAppPermissions, which is typically handled by a system permission dialog;
      * and another request for the content permission (e.g. through
      * requestContentPermission), which is typically handled by an app-specific
      * permission dialog.
      **/
     public interface PermissionDelegate {
@@ -1838,38 +1679,38 @@ public class GeckoView extends LayerView
              * implementation must call either grant() or reject() for every request.
              */
             void reject();
         }
 
         /**
          * Request Android app permissions.
          *
-         * @param view GeckoView instance requesting the permissions.
+         * @param view GeckoSession instance requesting the permissions.
          * @param permissions List of permissions to request; possible values are,
          *                    android.Manifest.permission.ACCESS_FINE_LOCATION
          *                    android.Manifest.permission.CAMERA
          *                    android.Manifest.permission.RECORD_AUDIO
          * @param callback Callback interface.
          */
-        void requestAndroidPermissions(GeckoView view, String[] permissions,
+        void requestAndroidPermissions(GeckoSession session, String[] permissions,
                                        Callback callback);
 
         /**
          * Request content permission.
          *
-         * @param view GeckoView instance requesting the permission.
+         * @param view GeckoSession instance requesting the permission.
          * @param uri The URI of the content requesting the permission.
          * @param type The type of the requested permission; possible values are,
          *             "geolocation": permission for using the geolocation API
          *             "desktop-notification": permission for using the notifications API
          * @param access Not used.
          * @param callback Callback interface.
          */
-        void requestContentPermission(GeckoView view, String uri, String type,
+        void requestContentPermission(GeckoSession session, String uri, String type,
                                       String access, Callback callback);
 
         /**
          * Callback interface for notifying the result of a media permission request,
          * including which media source(s) to use.
          */
         interface MediaCallback {
             /**
@@ -1902,17 +1743,17 @@ public class GeckoView extends LayerView
              */
             void reject();
         }
 
         /**
          * Request content media permissions, including request for which video and/or
          * audio source to use.
          *
-         * @param view GeckoView instance requesting the permission.
+         * @param view GeckoSession instance requesting the permission.
          * @param uri The URI of the content requesting the permission.
          * @param video List of video sources, or null if not requesting video.
          *              Each bundle represents a video source, with keys,
          *              "id": String, the origin-specific source identifier;
          *              "rawId": String, the non-origin-specific source identifier;
          *              "name": String, the name of the video source from the system
          *                      (for example, "Camera 0, Facing back, Orientation 90");
          *                      may be empty;
@@ -1923,12 +1764,12 @@ public class GeckoView extends LayerView
          * @param audio List of audio sources, or null if not requesting audio.
          *              Each bundle represents an audio source with same keys and possible
          *              values as video source bundles above, except for:
          *              "mediaSource", String; possible values are "microphone",
          *                             "audioCapture", and "other";
          *              "type", String, always "audio";
          * @param callback Callback interface.
          */
-        void requestMediaPermission(GeckoView view, String uri, GeckoBundle[] video,
+        void requestMediaPermission(GeckoSession session, String uri, GeckoBundle[] video,
                                     GeckoBundle[] audio, MediaCallback callback);
     }
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoViewSettings.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoViewSettings.java
@@ -85,17 +85,17 @@ public final class GeckoViewSettings imp
         mBundle = new GeckoBundle();
 
         setBoolean(USE_TRACKING_PROTECTION, false);
         setBoolean(USE_PRIVATE_MODE, false);
         setBoolean(USE_MULTIPROCESS, true);
         setInt(DISPLAY_MODE, DisplayMode.BROWSER.value());
         setBoolean(USE_REMOTE_DEBUGGER, false);
         // Set in GeckoView.init().
-        setString(DEBUGGER_SOCKET_DIR, "");
+        setString(DEBUGGER_SOCKET_DIR, ".");
     }
 
     /* package */ GeckoViewSettings(GeckoViewSettings settings, EventDispatcher eventDispatcher) {
         mBundle = new GeckoBundle(settings.mBundle);
         mEventDispatcher = eventDispatcher;
     }
 
     public void setBoolean(final Key<Boolean> key, boolean value) {
--- a/widget/android/GeneratedJNINatives.h
+++ b/widget/android/GeneratedJNINatives.h
@@ -214,16 +214,43 @@ template<class Impl>
 const JNINativeMethod GeckoScreenOrientation::Natives<Impl>::methods[] = {
 
     mozilla::jni::MakeNativeMethod<GeckoScreenOrientation::OnOrientationChange_t>(
             mozilla::jni::NativeStub<GeckoScreenOrientation::OnOrientationChange_t, Impl>
             ::template Wrap<&Impl::OnOrientationChange>)
 };
 
 template<class Impl>
+class GeckoSession::Window::Natives : public mozilla::jni::NativeImpl<Window, Impl>
+{
+public:
+    static const JNINativeMethod methods[4];
+};
+
+template<class Impl>
+const JNINativeMethod GeckoSession::Window::Natives<Impl>::methods[] = {
+
+    mozilla::jni::MakeNativeMethod<GeckoSession::Window::Attach_t>(
+            mozilla::jni::NativeStub<GeckoSession::Window::Attach_t, Impl>
+            ::template Wrap<&Impl::Attach>),
+
+    mozilla::jni::MakeNativeMethod<GeckoSession::Window::Close_t>(
+            mozilla::jni::NativeStub<GeckoSession::Window::Close_t, Impl>
+            ::template Wrap<&Impl::Close>),
+
+    mozilla::jni::MakeNativeMethod<GeckoSession::Window::DisposeNative_t>(
+            mozilla::jni::NativeStub<GeckoSession::Window::DisposeNative_t, Impl>
+            ::template Wrap<&Impl::DisposeNative>),
+
+    mozilla::jni::MakeNativeMethod<GeckoSession::Window::Open_t>(
+            mozilla::jni::NativeStub<GeckoSession::Window::Open_t, Impl>
+            ::template Wrap<&Impl::Open>)
+};
+
+template<class Impl>
 class GeckoThread::Natives : public mozilla::jni::NativeImpl<GeckoThread, Impl>
 {
 public:
     static const JNINativeMethod methods[7];
 };
 
 template<class Impl>
 const JNINativeMethod GeckoThread::Natives<Impl>::methods[] = {
@@ -253,43 +280,16 @@ const JNINativeMethod GeckoThread::Nativ
             ::template Wrap<&Impl::SpeculativeConnect>),
 
     mozilla::jni::MakeNativeMethod<GeckoThread::WaitOnGecko_t>(
             mozilla::jni::NativeStub<GeckoThread::WaitOnGecko_t, Impl>
             ::template Wrap<&Impl::WaitOnGecko>)
 };
 
 template<class Impl>
-class GeckoView::Window::Natives : public mozilla::jni::NativeImpl<Window, Impl>
-{
-public:
-    static const JNINativeMethod methods[4];
-};
-
-template<class Impl>
-const JNINativeMethod GeckoView::Window::Natives<Impl>::methods[] = {
-
-    mozilla::jni::MakeNativeMethod<GeckoView::Window::Attach_t>(
-            mozilla::jni::NativeStub<GeckoView::Window::Attach_t, Impl>
-            ::template Wrap<&Impl::Attach>),
-
-    mozilla::jni::MakeNativeMethod<GeckoView::Window::Close_t>(
-            mozilla::jni::NativeStub<GeckoView::Window::Close_t, Impl>
-            ::template Wrap<&Impl::Close>),
-
-    mozilla::jni::MakeNativeMethod<GeckoView::Window::DisposeNative_t>(
-            mozilla::jni::NativeStub<GeckoView::Window::DisposeNative_t, Impl>
-            ::template Wrap<&Impl::DisposeNative>),
-
-    mozilla::jni::MakeNativeMethod<GeckoView::Window::Open_t>(
-            mozilla::jni::NativeStub<GeckoView::Window::Open_t, Impl>
-            ::template Wrap<&Impl::Open>)
-};
-
-template<class Impl>
 class PrefsHelper::Natives : public mozilla::jni::NativeImpl<PrefsHelper, Impl>
 {
 public:
     static const JNINativeMethod methods[4];
 };
 
 template<class Impl>
 const JNINativeMethod PrefsHelper::Natives<Impl>::methods[] = {
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -764,16 +764,42 @@ constexpr char GeckoNetworkManager::OnSt
 constexpr char GeckoNetworkManager::OnStatusChanged_t::signature[];
 
 const char GeckoScreenOrientation::name[] =
         "org/mozilla/gecko/GeckoScreenOrientation";
 
 constexpr char GeckoScreenOrientation::OnOrientationChange_t::name[];
 constexpr char GeckoScreenOrientation::OnOrientationChange_t::signature[];
 
+const char GeckoSession::name[] =
+        "org/mozilla/gecko/GeckoSession";
+
+const char GeckoSession::Window::name[] =
+        "org/mozilla/gecko/GeckoSession$Window";
+
+constexpr char GeckoSession::Window::Attach_t::name[];
+constexpr char GeckoSession::Window::Attach_t::signature[];
+
+constexpr char GeckoSession::Window::Close_t::name[];
+constexpr char GeckoSession::Window::Close_t::signature[];
+
+constexpr char GeckoSession::Window::DisposeNative_t::name[];
+constexpr char GeckoSession::Window::DisposeNative_t::signature[];
+
+constexpr char GeckoSession::Window::OnReady_t::name[];
+constexpr char GeckoSession::Window::OnReady_t::signature[];
+
+auto GeckoSession::Window::OnReady() const -> void
+{
+    return mozilla::jni::Method<OnReady_t>::Call(Window::mCtx, nullptr);
+}
+
+constexpr char GeckoSession::Window::Open_t::name[];
+constexpr char GeckoSession::Window::Open_t::signature[];
+
 const char GeckoThread::name[] =
         "org/mozilla/gecko/GeckoThread";
 
 constexpr char GeckoThread::CheckAndSetState_t::name[];
 constexpr char GeckoThread::CheckAndSetState_t::signature[];
 
 auto GeckoThread::CheckAndSetState(mozilla::jni::Object::Param a0, mozilla::jni::Object::Param a1) -> bool
 {
@@ -945,69 +971,16 @@ auto GeckoThread::State::RESTARTING() ->
 constexpr char GeckoThread::State::RUNNING_t::name[];
 constexpr char GeckoThread::State::RUNNING_t::signature[];
 
 auto GeckoThread::State::RUNNING() -> State::LocalRef
 {
     return mozilla::jni::Field<RUNNING_t>::Get(State::Context(), nullptr);
 }
 
-const char GeckoView::name[] =
-        "org/mozilla/gecko/GeckoView";
-
-const char GeckoView::State::name[] =
-        "org/mozilla/gecko/GeckoView$State";
-
-constexpr char GeckoView::State::INITIAL_t::name[];
-constexpr char GeckoView::State::INITIAL_t::signature[];
-
-auto GeckoView::State::INITIAL() -> State::LocalRef
-{
-    return mozilla::jni::Field<INITIAL_t>::Get(State::Context(), nullptr);
-}
-
-constexpr char GeckoView::State::READY_t::name[];
-constexpr char GeckoView::State::READY_t::signature[];
-
-auto GeckoView::State::READY() -> State::LocalRef
-{
-    return mozilla::jni::Field<READY_t>::Get(State::Context(), nullptr);
-}
-
-const char GeckoView::Window::name[] =
-        "org/mozilla/gecko/GeckoView$Window";
-
-constexpr char GeckoView::Window::Attach_t::name[];
-constexpr char GeckoView::Window::Attach_t::signature[];
-
-constexpr char GeckoView::Window::Close_t::name[];
-constexpr char GeckoView::Window::Close_t::signature[];
-
-constexpr char GeckoView::Window::DisposeNative_t::name[];
-constexpr char GeckoView::Window::DisposeNative_t::signature[];
-
-constexpr char GeckoView::Window::OnAttach_t::name[];
-constexpr char GeckoView::Window::OnAttach_t::signature[];
-
-auto GeckoView::Window::OnAttach(GeckoView::Param a0) const -> void
-{
-    return mozilla::jni::Method<OnAttach_t>::Call(Window::mCtx, nullptr, a0);
-}
-
-constexpr char GeckoView::Window::Open_t::name[];
-constexpr char GeckoView::Window::Open_t::signature[];
-
-constexpr char GeckoView::Window::SetState_t::name[];
-constexpr char GeckoView::Window::SetState_t::signature[];
-
-auto GeckoView::Window::SetState(mozilla::jni::Object::Param a0) const -> void
-{
-    return mozilla::jni::Method<SetState_t>::Call(Window::mCtx, nullptr, a0);
-}
-
 const char PrefsHelper::name[] =
         "org/mozilla/gecko/PrefsHelper";
 
 constexpr char PrefsHelper::CallPrefHandler_t::name[];
 constexpr char PrefsHelper::CallPrefHandler_t::signature[];
 
 auto PrefsHelper::CallPrefHandler(mozilla::jni::Object::Param a0, int32_t a1, mozilla::jni::String::Param a2, bool a3, int32_t a4, mozilla::jni::String::Param a5) -> void
 {
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -2322,16 +2322,138 @@ public:
     };
 
     static const mozilla::jni::CallingThread callingThread =
             mozilla::jni::CallingThread::ANY;
 
     template<class Impl> class Natives;
 };
 
+class GeckoSession : public mozilla::jni::ObjectBase<GeckoSession>
+{
+public:
+    static const char name[];
+
+    explicit GeckoSession(const Context& ctx) : ObjectBase<GeckoSession>(ctx) {}
+
+    class Window;
+
+    static const mozilla::jni::CallingThread callingThread =
+            mozilla::jni::CallingThread::ANY;
+
+};
+
+class GeckoSession::Window : public mozilla::jni::ObjectBase<Window>
+{
+public:
+    static const char name[];
+
+    explicit Window(const Context& ctx) : ObjectBase<Window>(ctx) {}
+
+    struct Attach_t {
+        typedef Window Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<
+                mozilla::jni::Object::Param,
+                mozilla::jni::Object::Param> Args;
+        static constexpr char name[] = "attach";
+        static constexpr char signature[] =
+                "(Lorg/mozilla/gecko/GeckoView;Ljava/lang/Object;)V";
+        static const bool isStatic = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+        static const mozilla::jni::CallingThread callingThread =
+                mozilla::jni::CallingThread::ANY;
+        static const mozilla::jni::DispatchTarget dispatchTarget =
+                mozilla::jni::DispatchTarget::PROXY;
+    };
+
+    struct Close_t {
+        typedef Window Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "close";
+        static constexpr char signature[] =
+                "()V";
+        static const bool isStatic = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+        static const mozilla::jni::CallingThread callingThread =
+                mozilla::jni::CallingThread::ANY;
+        static const mozilla::jni::DispatchTarget dispatchTarget =
+                mozilla::jni::DispatchTarget::PROXY;
+    };
+
+    struct DisposeNative_t {
+        typedef Window Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "disposeNative";
+        static constexpr char signature[] =
+                "()V";
+        static const bool isStatic = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+        static const mozilla::jni::CallingThread callingThread =
+                mozilla::jni::CallingThread::ANY;
+        static const mozilla::jni::DispatchTarget dispatchTarget =
+                mozilla::jni::DispatchTarget::PROXY;
+    };
+
+    struct OnReady_t {
+        typedef Window Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "onReady";
+        static constexpr char signature[] =
+                "()V";
+        static const bool isStatic = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+        static const mozilla::jni::CallingThread callingThread =
+                mozilla::jni::CallingThread::GECKO;
+        static const mozilla::jni::DispatchTarget dispatchTarget =
+                mozilla::jni::DispatchTarget::CURRENT;
+    };
+
+    auto OnReady() const -> void;
+
+    struct Open_t {
+        typedef Window Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<
+                Window::Param,
+                mozilla::jni::Object::Param,
+                mozilla::jni::Object::Param,
+                mozilla::jni::String::Param,
+                int32_t,
+                bool> Args;
+        static constexpr char name[] = "open";
+        static constexpr char signature[] =
+                "(Lorg/mozilla/gecko/GeckoSession$Window;Lorg/mozilla/gecko/EventDispatcher;Lorg/mozilla/gecko/util/GeckoBundle;Ljava/lang/String;IZ)V";
+        static const bool isStatic = true;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+        static const mozilla::jni::CallingThread callingThread =
+                mozilla::jni::CallingThread::ANY;
+        static const mozilla::jni::DispatchTarget dispatchTarget =
+                mozilla::jni::DispatchTarget::PROXY;
+    };
+
+    static const mozilla::jni::CallingThread callingThread =
+            mozilla::jni::CallingThread::ANY;
+
+    template<class Impl> class Natives;
+};
+
 class GeckoThread : public mozilla::jni::ObjectBase<GeckoThread>
 {
 public:
     static const char name[];
 
     explicit GeckoThread(const Context& ctx) : ObjectBase<GeckoThread>(ctx) {}
 
     class State;
@@ -2822,211 +2944,16 @@ public:
 
     static auto RUNNING() -> State::LocalRef;
 
     static const mozilla::jni::CallingThread callingThread =
             mozilla::jni::CallingThread::ANY;
 
 };
 
-class GeckoView : public mozilla::jni::ObjectBase<GeckoView>
-{
-public:
-    static const char name[];
-
-    explicit GeckoView(const Context& ctx) : ObjectBase<GeckoView>(ctx) {}
-
-    class State;
-    class Window;
-
-    static const mozilla::jni::CallingThread callingThread =
-            mozilla::jni::CallingThread::ANY;
-
-};
-
-class GeckoView::State : public mozilla::jni::ObjectBase<State>
-{
-public:
-    static const char name[];
-
-    explicit State(const Context& ctx) : ObjectBase<State>(ctx) {}
-
-    struct INITIAL_t {
-        typedef State Owner;
-        typedef State::LocalRef ReturnType;
-        typedef State::Param SetterType;
-        typedef mozilla::jni::Args<> Args;
-        static constexpr char name[] = "INITIAL";
-        static constexpr char signature[] =
-                "Lorg/mozilla/gecko/GeckoView$State;";
-        static const bool isStatic = true;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-        static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::ANY;
-        static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::CURRENT;
-    };
-
-    static auto INITIAL() -> State::LocalRef;
-
-    struct READY_t {
-        typedef State Owner;
-        typedef State::LocalRef ReturnType;
-        typedef State::Param SetterType;
-        typedef mozilla::jni::Args<> Args;
-        static constexpr char name[] = "READY";
-        static constexpr char signature[] =
-                "Lorg/mozilla/gecko/GeckoView$State;";
-        static const bool isStatic = true;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-        static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::ANY;
-        static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::CURRENT;
-    };
-
-    static auto READY() -> State::LocalRef;
-
-    static const mozilla::jni::CallingThread callingThread =
-            mozilla::jni::CallingThread::ANY;
-
-};
-
-class GeckoView::Window : public mozilla::jni::ObjectBase<Window>
-{
-public:
-    static const char name[];
-
-    explicit Window(const Context& ctx) : ObjectBase<Window>(ctx) {}
-
-    struct Attach_t {
-        typedef Window Owner;
-        typedef void ReturnType;
-        typedef void SetterType;
-        typedef mozilla::jni::Args<
-                GeckoView::Param,
-                mozilla::jni::Object::Param,
-                mozilla::jni::Object::Param> Args;
-        static constexpr char name[] = "attach";
-        static constexpr char signature[] =
-                "(Lorg/mozilla/gecko/GeckoView;Ljava/lang/Object;Lorg/mozilla/gecko/EventDispatcher;)V";
-        static const bool isStatic = false;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-        static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::ANY;
-        static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::PROXY;
-    };
-
-    struct Close_t {
-        typedef Window Owner;
-        typedef void ReturnType;
-        typedef void SetterType;
-        typedef mozilla::jni::Args<> Args;
-        static constexpr char name[] = "close";
-        static constexpr char signature[] =
-                "()V";
-        static const bool isStatic = false;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-        static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::ANY;
-        static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::PROXY;
-    };
-
-    struct DisposeNative_t {
-        typedef Window Owner;
-        typedef void ReturnType;
-        typedef void SetterType;
-        typedef mozilla::jni::Args<> Args;
-        static constexpr char name[] = "disposeNative";
-        static constexpr char signature[] =
-                "()V";
-        static const bool isStatic = false;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-        static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::ANY;
-        static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::PROXY;
-    };
-
-    struct OnAttach_t {
-        typedef Window Owner;
-        typedef void ReturnType;
-        typedef void SetterType;
-        typedef mozilla::jni::Args<
-                GeckoView::Param> Args;
-        static constexpr char name[] = "onAttach";
-        static constexpr char signature[] =
-                "(Lorg/mozilla/gecko/GeckoView;)V";
-        static const bool isStatic = false;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-        static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::GECKO;
-        static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::CURRENT;
-    };
-
-    auto OnAttach(GeckoView::Param) const -> void;
-
-    struct Open_t {
-        typedef Window Owner;
-        typedef void ReturnType;
-        typedef void SetterType;
-        typedef mozilla::jni::Args<
-                Window::Param,
-                mozilla::jni::Object::Param,
-                mozilla::jni::Object::Param,
-                mozilla::jni::String::Param,
-                int32_t,
-                bool> Args;
-        static constexpr char name[] = "open";
-        static constexpr char signature[] =
-                "(Lorg/mozilla/gecko/GeckoView$Window;Lorg/mozilla/gecko/EventDispatcher;Lorg/mozilla/gecko/util/GeckoBundle;Ljava/lang/String;IZ)V";
-        static const bool isStatic = true;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-        static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::ANY;
-        static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::PROXY;
-    };
-
-    struct SetState_t {
-        typedef Window Owner;
-        typedef void ReturnType;
-        typedef void SetterType;
-        typedef mozilla::jni::Args<
-                mozilla::jni::Object::Param> Args;
-        static constexpr char name[] = "setState";
-        static constexpr char signature[] =
-                "(Lorg/mozilla/gecko/GeckoView$State;)V";
-        static const bool isStatic = false;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-        static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::GECKO;
-        static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::CURRENT;
-    };
-
-    auto SetState(mozilla::jni::Object::Param) const -> void;
-
-    static const mozilla::jni::CallingThread callingThread =
-            mozilla::jni::CallingThread::ANY;
-
-    template<class Impl> class Natives;
-};
-
 class PrefsHelper : public mozilla::jni::ObjectBase<PrefsHelper>
 {
 public:
     static const char name[];
 
     explicit PrefsHelper(const Context& ctx) : ObjectBase<PrefsHelper>(ctx) {}
 
     struct CallPrefHandler_t {
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -227,24 +227,24 @@ public:
         , mImpl(aPtr.mImpl)
     {}
 
     operator Impl*() const { return mImpl; }
     Impl* operator->() const { return mImpl; }
 };
 
 class nsWindow::GeckoViewSupport final
-    : public GeckoView::Window::Natives<GeckoViewSupport>
+    : public GeckoSession::Window::Natives<GeckoViewSupport>
     , public SupportsWeakPtr<GeckoViewSupport>
 {
     nsWindow& window;
-    GeckoView::Window::GlobalRef mGeckoViewWindow;
+    GeckoSession::Window::GlobalRef mGeckoViewWindow;
 
 public:
-    typedef GeckoView::Window::Natives<GeckoViewSupport> Base;
+    typedef GeckoSession::Window::Natives<GeckoViewSupport> Base;
     typedef SupportsWeakPtr<GeckoViewSupport> SupportsWeakPtr;
 
     MOZ_DECLARE_WEAKREFERENCE_TYPENAME(GeckoViewSupport);
 
     template<typename Functor>
     static void OnNativeCall(Functor&& aCall)
     {
         if (aCall.IsTarget(&Open) && NS_IsMainThread()) {
@@ -253,17 +253,17 @@ public:
             // can get a head start on opening our window.
             return aCall();
         }
 
         NS_DispatchToMainThread(new WindowEvent<Functor>(mozilla::Move(aCall)));
     }
 
     GeckoViewSupport(nsWindow* aWindow,
-                     const GeckoView::Window::LocalRef& aInstance)
+                     const GeckoSession::Window::LocalRef& aInstance)
         : window(*aWindow)
         , mGeckoViewWindow(aInstance)
     {
         Base::AttachNative(aInstance, static_cast<SupportsWeakPtr*>(this));
     }
 
     ~GeckoViewSupport();
 
@@ -273,30 +273,29 @@ public:
      * GeckoView methods
      */
 private:
     nsCOMPtr<nsPIDOMWindowOuter> mDOMWindow;
 
 public:
     // Create and attach a window.
     static void Open(const jni::Class::LocalRef& aCls,
-                     GeckoView::Window::Param aWindow,
+                     GeckoSession::Window::Param aWindow,
                      jni::Object::Param aDispatcher,
                      jni::Object::Param aSettings,
                      jni::String::Param aChromeURI,
                      int32_t aScreenId,
                      bool aPrivateMode);
 
     // Close and destroy the nsWindow.
     void Close();
 
     // Reattach this nsWindow to a new GeckoView.
-    void Attach(const GeckoView::Window::LocalRef& inst,
-                GeckoView::Param aView, jni::Object::Param aCompositor,
-                jni::Object::Param aDispatcher);
+    void Attach(const GeckoSession::Window::LocalRef& inst,
+                jni::Object::Param aView, jni::Object::Param aCompositor);
 
     void EnableEventDispatcher();
 };
 
 /**
  * NativePanZoomController handles its native calls on the UI thread, so make
  * it separate from GeckoViewSupport.
  */
@@ -1256,17 +1255,17 @@ nsWindow::GeckoViewSupport::~GeckoViewSu
 
     if (window.mLayerViewSupport) {
         window.mLayerViewSupport.Detach();
     }
 }
 
 /* static */ void
 nsWindow::GeckoViewSupport::Open(const jni::Class::LocalRef& aCls,
-                                 GeckoView::Window::Param aWindow,
+                                 GeckoSession::Window::Param aWindow,
                                  jni::Object::Param aDispatcher,
                                  jni::Object::Param aSettings,
                                  jni::String::Param aChromeURI,
                                  int32_t aScreenId,
                                  bool aPrivateMode)
 {
     MOZ_ASSERT(NS_IsMainThread());
 
@@ -1306,17 +1305,17 @@ nsWindow::GeckoViewSupport::Open(const j
     nsCOMPtr<nsIWidget> widget = WidgetUtils::DOMWindowToWidget(pdomWindow);
     MOZ_ASSERT(widget);
 
     const auto window = static_cast<nsWindow*>(widget.get());
     window->SetScreenId(aScreenId);
 
     // Attach a new GeckoView support object to the new window.
     window->mGeckoViewSupport = mozilla::MakeUnique<GeckoViewSupport>(
-        window, (GeckoView::Window::LocalRef(aCls.Env(), aWindow)));
+        window, (GeckoSession::Window::LocalRef(aCls.Env(), aWindow)));
 
     window->mGeckoViewSupport->mDOMWindow = pdomWindow;
 
     // Attach a new GeckoEditable support object to the new window.
     auto editable = GeckoEditable::New();
     auto editableChild = GeckoEditableChild::New(editable);
     editable->SetDefaultEditableChild(editableChild);
     window->mEditable = editable;
@@ -1350,45 +1349,40 @@ nsWindow::GeckoViewSupport::Close()
     }
 
     mDOMWindow->ForceClose();
     mDOMWindow = nullptr;
     mGeckoViewWindow = nullptr;
 }
 
 void
-nsWindow::GeckoViewSupport::Attach(const GeckoView::Window::LocalRef& inst,
-                                   GeckoView::Param aView,
-                                   jni::Object::Param aCompositor,
-                                   jni::Object::Param aDispatcher)
+nsWindow::GeckoViewSupport::Attach(const GeckoSession::Window::LocalRef& inst,
+                                   jni::Object::Param aView,
+                                   jni::Object::Param aCompositor)
 {
     // Associate our previous GeckoEditable with the new GeckoView.
     MOZ_ASSERT(window.mEditable);
     window.mEditable->OnViewChange(aView);
 
     // mNPZCSupport might have already been detached through the Java side calling
     // NativePanZoomController.destroy().
     if (window.mNPZCSupport) {
         window.mNPZCSupport.Detach();
     }
 
     if (window.mLayerViewSupport) {
         window.mLayerViewSupport.Detach();
     }
 
-    auto compositor = LayerView::Compositor::LocalRef(
-            inst.Env(), LayerView::Compositor::Ref::From(aCompositor));
-    window.mLayerViewSupport.Attach(compositor, &window, compositor);
-    compositor->Reattach();
-
-    MOZ_ASSERT(window.mAndroidView);
-    window.mAndroidView->mEventDispatcher->Attach(
-            java::EventDispatcher::Ref::From(aDispatcher), mDOMWindow);
-
-    mGeckoViewWindow->OnAttach(aView);
+    if (aCompositor) {
+        auto compositor = LayerView::Compositor::LocalRef(
+                inst.Env(), LayerView::Compositor::Ref::From(aCompositor));
+        window.mLayerViewSupport.Attach(compositor, &window, compositor);
+        compositor->Reattach();
+    }
 }
 
 void
 nsWindow::InitNatives()
 {
     nsWindow::GeckoViewSupport::Base::Init();
     nsWindow::LayerViewSupport::Init();
     nsWindow::NPZCSupport::Init();
@@ -2113,17 +2107,17 @@ nsWindow::GetEventTimeStamp(int64_t aEve
 }
 
 void
 nsWindow::GeckoViewSupport::EnableEventDispatcher()
 {
     if (!mGeckoViewWindow) {
         return;
     }
-    mGeckoViewWindow->SetState(GeckoView::State::READY());
+    mGeckoViewWindow->OnReady();
 }
 
 void
 nsWindow::UserActivity()
 {
   if (!mIdleService) {
     mIdleService = do_GetService("@mozilla.org/widget/idleservice;1");
   }